Files
MaxScale/server/core/resultset.cc
Markus Mäkelä bd48db28ec MXS-1929: Simplify result set mechanism
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.
2018-07-31 22:32:32 +03:00

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);
}