The result set mechanism was ill-suited for iteration over lists. Converting it into a class and inverting it by pushing rows into the result set instead the result set asking for rows makes it very easy to use with lists. It also solves some of the consistency problems that existed with the previous implementation.
		
			
				
	
	
		
			210 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			210 lines
		
	
	
		
			5.6 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: 2022-01-01
 | 
						|
 *
 | 
						|
 * 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.
 | 
						|
 */
 | 
						|
 | 
						|
#include <string.h>
 | 
						|
#include <ctype.h>
 | 
						|
 | 
						|
#include <numeric>
 | 
						|
 | 
						|
#include <maxscale/resultset.hh>
 | 
						|
#include <maxscale/buffer.h>
 | 
						|
#include <maxscale/dcb.h>
 | 
						|
#include <maxscale/mysql_binlog.h>
 | 
						|
#include <maxscale/protocol/mysql.h>
 | 
						|
 | 
						|
/**
 | 
						|
 * Send the field count packet in a response packet sequence.
 | 
						|
 *
 | 
						|
 * @param dcb           DCB of connection to send result set to
 | 
						|
 * @param count         Number of columns in the result set
 | 
						|
 * @return              Non-zero on success
 | 
						|
 */
 | 
						|
static int mysql_send_fieldcount(DCB *dcb, int count)
 | 
						|
{
 | 
						|
    GWBUF *pkt;
 | 
						|
    uint8_t *ptr;
 | 
						|
 | 
						|
    if ((pkt = gwbuf_alloc(5)) == NULL)
 | 
						|
    {
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
    ptr = GWBUF_DATA(pkt);
 | 
						|
    *ptr++ = 0x01;                  // Payload length
 | 
						|
    *ptr++ = 0x00;
 | 
						|
    *ptr++ = 0x00;
 | 
						|
    *ptr++ = 0x01;                  // Sequence number in response
 | 
						|
    *ptr++ = count;                 // Length of result string
 | 
						|
    return dcb->func.write(dcb, pkt);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Send the column definition packet in a response packet sequence.
 | 
						|
 *
 | 
						|
 * @param dcb           The DCB of the connection
 | 
						|
 * @param name          Name of the column
 | 
						|
 * @param type          Column type
 | 
						|
 * @param len           Column length
 | 
						|
 * @param seqno         Packet sequence number
 | 
						|
 * @return              Non-zero on success
 | 
						|
 */
 | 
						|
static int mysql_send_columndef(DCB *dcb, const std::string& name, uint8_t seqno)
 | 
						|
{
 | 
						|
    GWBUF* pkt = gwbuf_alloc(26 + name.length());
 | 
						|
 | 
						|
    if (pkt == NULL)
 | 
						|
    {
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    int len = 255; // Column type length e.g. VARCHAR(255)
 | 
						|
 | 
						|
    uint8_t* ptr = GWBUF_DATA(pkt);
 | 
						|
    int plen = 22 + name.length();
 | 
						|
    *ptr++ = plen & 0xff;
 | 
						|
    *ptr++ = (plen >> 8) & 0xff;
 | 
						|
    *ptr++ = (plen >> 16) & 0xff;
 | 
						|
    *ptr++ = seqno;                         // Sequence number in response
 | 
						|
    *ptr++ = 3;                             // Catalog is always def
 | 
						|
    *ptr++ = 'd';
 | 
						|
    *ptr++ = 'e';
 | 
						|
    *ptr++ = 'f';
 | 
						|
    *ptr++ = 0;                             // Schema name length
 | 
						|
    *ptr++ = 0;                             // virtual table name length
 | 
						|
    *ptr++ = 0;                             // Table name length
 | 
						|
    *ptr++ = name.length();                 // Column name length;
 | 
						|
    memcpy(ptr, name.c_str(), name.length());
 | 
						|
    ptr += name.length();
 | 
						|
    *ptr++ = 0;                             // Original column name
 | 
						|
    *ptr++ = 0x0c;                          // Length of next fields always 12
 | 
						|
    *ptr++ = 0x3f;                          // Character set
 | 
						|
    *ptr++ = 0;
 | 
						|
    *ptr++ = len & 0xff;                    // Length of column
 | 
						|
    *ptr++ = (len >> 8) & 0xff;
 | 
						|
    *ptr++ = (len >> 16) & 0xff;
 | 
						|
    *ptr++ = (len >> 24) & 0xff;
 | 
						|
    *ptr++ = TABLE_COL_TYPE_VARCHAR;
 | 
						|
    *ptr++ = 0x81;                          // Two bytes of flags
 | 
						|
    *ptr++ = 0x00;
 | 
						|
    *ptr++ = 0;
 | 
						|
    *ptr++ = 0;
 | 
						|
    *ptr++ = 0;
 | 
						|
    return dcb->func.write(dcb, pkt);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Send an EOF packet in a response packet sequence.
 | 
						|
 *
 | 
						|
 * @param dcb           The client connection
 | 
						|
 * @param seqno         The sequence number of the EOF packet
 | 
						|
 * @return              Non-zero on success
 | 
						|
 */
 | 
						|
static int mysql_send_eof(DCB *dcb, int seqno)
 | 
						|
{
 | 
						|
    GWBUF* pkt = gwbuf_alloc(9);
 | 
						|
 | 
						|
    if (pkt == NULL)
 | 
						|
    {
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    uint8_t* ptr = GWBUF_DATA(pkt);
 | 
						|
    *ptr++ = 0x05;
 | 
						|
    *ptr++ = 0x00;
 | 
						|
    *ptr++ = 0x00;
 | 
						|
    *ptr++ = seqno;                         // Sequence number in response
 | 
						|
    *ptr++ = 0xfe;                          // Length of result string
 | 
						|
    *ptr++ = 0x00;                          // No Errors
 | 
						|
    *ptr++ = 0x00;
 | 
						|
    *ptr++ = 0x02;                          // Autocommit enabled
 | 
						|
    *ptr++ = 0x00;
 | 
						|
    return dcb->func.write(dcb, pkt);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Send a row packet in a response packet sequence.
 | 
						|
 *
 | 
						|
 * @param dcb           The client connection
 | 
						|
 * @param row           The row to send
 | 
						|
 * @param seqno         The sequence number of the EOF packet
 | 
						|
 * @return              Non-zero on success
 | 
						|
 */
 | 
						|
static int mysql_send_row(DCB *dcb, const std::vector<std::string>& row, int seqno)
 | 
						|
{
 | 
						|
    auto acc = [](int l, const std::string& s)
 | 
						|
    {
 | 
						|
        return l + s.length() + 1;
 | 
						|
    };
 | 
						|
 | 
						|
    int len = std::accumulate(row.begin(), row.end(), MYSQL_HEADER_LEN, acc);
 | 
						|
 | 
						|
    GWBUF* pkt = gwbuf_alloc(len);
 | 
						|
 | 
						|
    if (pkt == NULL)
 | 
						|
    {
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    uint8_t* ptr = GWBUF_DATA(pkt);
 | 
						|
    len -= MYSQL_HEADER_LEN;
 | 
						|
    *ptr++ = len & 0xff;
 | 
						|
    *ptr++ = (len >> 8) & 0xff;
 | 
						|
    *ptr++ = (len >> 16) & 0xff;
 | 
						|
    *ptr++ = seqno;
 | 
						|
 | 
						|
    for (auto&& a : row)
 | 
						|
    {
 | 
						|
        *ptr++ = a.length();
 | 
						|
        memcpy(ptr, a.c_str(), a.length());
 | 
						|
        ptr += a.length();
 | 
						|
    }
 | 
						|
 | 
						|
    return dcb->func.write(dcb, pkt);
 | 
						|
}
 | 
						|
 | 
						|
ResultSet::ResultSet(std::initializer_list<std::string> names):
 | 
						|
    m_columns(names)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
std::unique_ptr<ResultSet> ResultSet::create(std::initializer_list<std::string> names)
 | 
						|
{
 | 
						|
    return std::unique_ptr<ResultSet>(new (std::nothrow) ResultSet(names));
 | 
						|
}
 | 
						|
 | 
						|
void ResultSet::add_row(std::initializer_list<std::string> values)
 | 
						|
{
 | 
						|
    ss_dassert(values.size() == m_columns.size());
 | 
						|
    m_rows.emplace_back(values);
 | 
						|
}
 | 
						|
 | 
						|
void ResultSet::write(DCB* dcb)
 | 
						|
{
 | 
						|
    mysql_send_fieldcount(dcb, m_columns.size());
 | 
						|
 | 
						|
    uint8_t seqno = 2; // The second packet after field count
 | 
						|
 | 
						|
    for (auto&& c : m_columns)
 | 
						|
    {
 | 
						|
        mysql_send_columndef(dcb, c, seqno++);
 | 
						|
    }
 | 
						|
 | 
						|
    mysql_send_eof(dcb, seqno++);
 | 
						|
 | 
						|
    for (auto&& r : m_rows)
 | 
						|
    {
 | 
						|
        mysql_send_row(dcb, r, seqno++);
 | 
						|
    }
 | 
						|
 | 
						|
    mysql_send_eof(dcb, seqno);
 | 
						|
}
 |