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.
This commit is contained in:
parent
28e55b260f
commit
bd48db28ec
@ -1,82 +0,0 @@
|
||||
#pragma once
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file resultset.h The MaxScale generic result set mechanism
|
||||
*/
|
||||
|
||||
#include <maxscale/cdefs.h>
|
||||
#include <maxscale/dcb.h>
|
||||
|
||||
MXS_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* Column types
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
COL_TYPE_VARCHAR = 0x0f,
|
||||
COL_TYPE_VARSTRING = 0xfd
|
||||
} RESULT_COL_TYPE;
|
||||
|
||||
/**
|
||||
* The result set column definition. Each result set has an order linked
|
||||
* list of column definitions.
|
||||
*/
|
||||
typedef struct resultcolumn
|
||||
{
|
||||
char *name; /*< Column name */
|
||||
int len; /*< Column length */
|
||||
RESULT_COL_TYPE type; /*< Column type */
|
||||
struct resultcolumn *next; /*< next column */
|
||||
} RESULT_COLUMN;
|
||||
|
||||
/**
|
||||
* A representation of a row within a result set.
|
||||
*/
|
||||
typedef struct resultrow
|
||||
{
|
||||
int n_cols; /*< No. of columns in row */
|
||||
char **cols; /*< The columns themselves */
|
||||
} RESULT_ROW;
|
||||
|
||||
struct resultset;
|
||||
|
||||
/**
|
||||
* Type of callback function used to supply each row
|
||||
*/
|
||||
typedef RESULT_ROW * (*RESULT_ROW_CB)(struct resultset *, void *);
|
||||
|
||||
/**
|
||||
* The representation of the result set itself.
|
||||
*/
|
||||
typedef struct resultset
|
||||
{
|
||||
int n_cols; /*< No. of columns */
|
||||
RESULT_COLUMN *column; /*< Linked list of column definitions */
|
||||
RESULT_ROW_CB fetchrow; /*< Fetch a row for the result set */
|
||||
void *userdata; /*< User data for the fetch row call */
|
||||
} RESULTSET;
|
||||
|
||||
extern RESULTSET *resultset_create(RESULT_ROW_CB, void *);
|
||||
extern void resultset_free(RESULTSET *);
|
||||
extern int resultset_add_column(RESULTSET *, const char *, int, RESULT_COL_TYPE);
|
||||
extern void resultset_column_free(RESULT_COLUMN *);
|
||||
extern RESULT_ROW *resultset_make_row(RESULTSET *);
|
||||
extern void resultset_free_row(RESULT_ROW *);
|
||||
extern int resultset_row_set(RESULT_ROW *, int, const char *);
|
||||
extern void resultset_stream_mysql(RESULTSET *, DCB *);
|
||||
extern void resultset_stream_json(RESULTSET *, DCB *);
|
||||
|
||||
MXS_END_DECLS
|
60
include/maxscale/resultset.hh
Normal file
60
include/maxscale/resultset.hh
Normal file
@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
/*
|
||||
* 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 <maxscale/cppdefs.hh>
|
||||
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <maxscale/dcb.h>
|
||||
#include <maxscale/mysql_binlog.h>
|
||||
|
||||
/**
|
||||
* A result set consisting of VARCHAR(255) columns
|
||||
*/
|
||||
class ResultSet
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Create a new result set
|
||||
*
|
||||
* @param names List of column names
|
||||
*
|
||||
* @return The new result set
|
||||
*/
|
||||
static std::unique_ptr<ResultSet> create(std::initializer_list<std::string> names);
|
||||
|
||||
/**
|
||||
* Add a row to the result set
|
||||
*
|
||||
* @param values List of values for the row
|
||||
*/
|
||||
void add_row(std::initializer_list<std::string> values);
|
||||
|
||||
/**
|
||||
* Write the result set to a DCB
|
||||
*
|
||||
* @param dcb DCB where the result set is written
|
||||
*/
|
||||
void write(DCB* dcb);
|
||||
|
||||
private:
|
||||
std::vector<std::string> m_columns;
|
||||
std::vector<std::vector<std::string>> m_rows;
|
||||
|
||||
ResultSet(std::initializer_list<std::string> names);
|
||||
};
|
@ -11,255 +11,16 @@
|
||||
* Public License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file resultset.c - Implementation of a generic result set mechanism
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 17/02/15 Mark Riddoch Initial implementation
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <maxscale/alloc.h>
|
||||
#include <maxscale/resultset.h>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include <maxscale/resultset.hh>
|
||||
#include <maxscale/buffer.h>
|
||||
#include <maxscale/dcb.h>
|
||||
|
||||
|
||||
static int mysql_send_fieldcount(DCB *, int);
|
||||
static int mysql_send_columndef(DCB *, const char *, int, int, uint8_t);
|
||||
static int mysql_send_eof(DCB *, int);
|
||||
static int mysql_send_row(DCB *, RESULT_ROW *, int);
|
||||
|
||||
|
||||
/**
|
||||
* Create a generic result set
|
||||
*
|
||||
* @param func Function to call for each row
|
||||
* @param data Data to pass to the row retrieval function
|
||||
* @return An empty resultset or NULL on error
|
||||
*/
|
||||
RESULTSET *
|
||||
resultset_create(RESULT_ROW_CB func, void *data)
|
||||
{
|
||||
RESULTSET *rval = (RESULTSET *)MXS_MALLOC(sizeof(RESULTSET));
|
||||
|
||||
if (rval)
|
||||
{
|
||||
rval->n_cols = 0;
|
||||
rval->column = NULL;
|
||||
rval->userdata = data;
|
||||
rval->fetchrow = func;
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a previously allocated resultset
|
||||
*
|
||||
* @param resultset The result set to free
|
||||
*/
|
||||
void
|
||||
resultset_free(RESULTSET *resultset)
|
||||
{
|
||||
RESULT_COLUMN *col;
|
||||
|
||||
if (resultset != NULL)
|
||||
{
|
||||
col = resultset->column;
|
||||
while (col)
|
||||
{
|
||||
RESULT_COLUMN *next;
|
||||
|
||||
next = col->next;
|
||||
resultset_column_free(col);
|
||||
col = next;
|
||||
}
|
||||
MXS_FREE(resultset);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new column to a result set. Columns are added to the right
|
||||
* of the result set, i.e. the existing order is maintained.
|
||||
*
|
||||
* @param set The result set
|
||||
* @param name The column name
|
||||
* @param len The column length
|
||||
* @param type The column type
|
||||
* @return The numebr of columns added to the result set
|
||||
*/
|
||||
int
|
||||
resultset_add_column(RESULTSET *set, const char *cname, int len, RESULT_COL_TYPE type)
|
||||
{
|
||||
char *name = MXS_STRDUP(cname);
|
||||
RESULT_COLUMN *newcol = (RESULT_COLUMN *)MXS_MALLOC(sizeof(RESULT_COLUMN));
|
||||
|
||||
if (!name || !newcol)
|
||||
{
|
||||
MXS_FREE(name);
|
||||
MXS_FREE(newcol);
|
||||
return 0;
|
||||
}
|
||||
|
||||
newcol->name = name;
|
||||
newcol->type = type;
|
||||
newcol->len = len;
|
||||
newcol->next = NULL;
|
||||
|
||||
if (set->column == NULL)
|
||||
{
|
||||
set->column = newcol;
|
||||
}
|
||||
else
|
||||
{
|
||||
RESULT_COLUMN *ptr = set->column;
|
||||
while (ptr->next)
|
||||
{
|
||||
ptr = ptr->next;
|
||||
}
|
||||
ptr->next = newcol;
|
||||
}
|
||||
set->n_cols++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a result set column
|
||||
*
|
||||
* @param col Column to free
|
||||
*/
|
||||
void
|
||||
resultset_column_free(RESULT_COLUMN *col)
|
||||
{
|
||||
MXS_FREE(col->name);
|
||||
MXS_FREE(col);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a blank row, a row with all values NULL, for a result
|
||||
* set.
|
||||
*
|
||||
* @param set The result set the row will be part of
|
||||
* @return The NULL result set row
|
||||
*/
|
||||
RESULT_ROW *
|
||||
resultset_make_row(RESULTSET *set)
|
||||
{
|
||||
RESULT_ROW *row;
|
||||
int i;
|
||||
|
||||
if ((row = (RESULT_ROW *)MXS_MALLOC(sizeof(RESULT_ROW))) == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
row->n_cols = set->n_cols;
|
||||
if ((row->cols = (char **)MXS_MALLOC(row->n_cols * sizeof(char *))) == NULL)
|
||||
{
|
||||
MXS_FREE(row);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < set->n_cols; i++)
|
||||
{
|
||||
row->cols[i] = NULL;
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a result set row. If a column in the row has a non-null values
|
||||
* then the data is assumed to be a malloc'd pointer and will be free'd.
|
||||
* If any value is not a malloc'd pointer it should be removed before
|
||||
* making this call.
|
||||
*
|
||||
* @param row The row to free
|
||||
*/
|
||||
void
|
||||
resultset_free_row(RESULT_ROW *row)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < row->n_cols; i++)
|
||||
{
|
||||
if (row->cols[i])
|
||||
{
|
||||
MXS_FREE(row->cols[i]);
|
||||
}
|
||||
}
|
||||
MXS_FREE(row->cols);
|
||||
MXS_FREE(row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a value in a particular column of the row . The value is
|
||||
* a NULL terminated string and will be copied into malloc'd
|
||||
* storage by this routine.
|
||||
*
|
||||
* @param row The row ro add the column into
|
||||
* @param col The column number (0 to n_cols - 1)
|
||||
* @param value The column value, may be NULL
|
||||
* @return The number of columns inserted
|
||||
*/
|
||||
int
|
||||
resultset_row_set(RESULT_ROW *row, int col, const char *value)
|
||||
{
|
||||
if (col < 0 || col >= row->n_cols)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (value)
|
||||
{
|
||||
if ((row->cols[col] = MXS_STRDUP(value)) == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else if (row->cols[col])
|
||||
{
|
||||
MXS_FREE(row->cols[col]);
|
||||
}
|
||||
row->cols[col] = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream a result set using the MySQL protocol for encodign the result
|
||||
* set. Each row is retrieved by calling the function passed in the
|
||||
* argument list.
|
||||
*
|
||||
* @param set The result set to stream
|
||||
* @param dcb The connection to stream the result set to
|
||||
*/
|
||||
void
|
||||
resultset_stream_mysql(RESULTSET *set, DCB *dcb)
|
||||
{
|
||||
RESULT_COLUMN *col;
|
||||
RESULT_ROW *row;
|
||||
uint8_t seqno = 2;
|
||||
|
||||
mysql_send_fieldcount(dcb, set->n_cols);
|
||||
|
||||
col = set->column;
|
||||
while (col)
|
||||
{
|
||||
mysql_send_columndef(dcb, col->name, col->type, col->len, seqno++);
|
||||
col = col->next;
|
||||
}
|
||||
mysql_send_eof(dcb, seqno++);
|
||||
while ((row = (*set->fetchrow)(set, set->userdata)) != NULL)
|
||||
{
|
||||
mysql_send_row(dcb, row, seqno++);
|
||||
resultset_free_row(row);
|
||||
}
|
||||
mysql_send_eof(dcb, seqno);
|
||||
}
|
||||
#include <maxscale/mysql_binlog.h>
|
||||
#include <maxscale/protocol/mysql.h>
|
||||
|
||||
/**
|
||||
* Send the field count packet in a response packet sequence.
|
||||
@ -268,8 +29,7 @@ resultset_stream_mysql(RESULTSET *set, DCB *dcb)
|
||||
* @param count Number of columns in the result set
|
||||
* @return Non-zero on success
|
||||
*/
|
||||
static int
|
||||
mysql_send_fieldcount(DCB *dcb, int count)
|
||||
static int mysql_send_fieldcount(DCB *dcb, int count)
|
||||
{
|
||||
GWBUF *pkt;
|
||||
uint8_t *ptr;
|
||||
@ -287,7 +47,6 @@ mysql_send_fieldcount(DCB *dcb, int count)
|
||||
return dcb->func.write(dcb, pkt);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send the column definition packet in a response packet sequence.
|
||||
*
|
||||
@ -298,19 +57,19 @@ mysql_send_fieldcount(DCB *dcb, int count)
|
||||
* @param seqno Packet sequence number
|
||||
* @return Non-zero on success
|
||||
*/
|
||||
static int
|
||||
mysql_send_columndef(DCB *dcb, const char *name, int type, int len, uint8_t seqno)
|
||||
static int mysql_send_columndef(DCB *dcb, const std::string& name, uint8_t seqno)
|
||||
{
|
||||
GWBUF *pkt;
|
||||
uint8_t *ptr;
|
||||
int plen;
|
||||
GWBUF* pkt = gwbuf_alloc(26 + name.length());
|
||||
|
||||
if ((pkt = gwbuf_alloc(26 + strlen(name))) == NULL)
|
||||
if (pkt == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
ptr = GWBUF_DATA(pkt);
|
||||
plen = 22 + strlen(name);
|
||||
|
||||
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;
|
||||
@ -322,12 +81,10 @@ mysql_send_columndef(DCB *dcb, const char *name, int type, int len, uint8_t seqn
|
||||
*ptr++ = 0; // Schema name length
|
||||
*ptr++ = 0; // virtual table name length
|
||||
*ptr++ = 0; // Table name length
|
||||
*ptr++ = strlen(name); // Column name length;
|
||||
while (*name)
|
||||
{
|
||||
*ptr++ = *name++; // Copy the column name
|
||||
}
|
||||
*ptr++ = 0; // Orginal column name
|
||||
*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;
|
||||
@ -335,23 +92,15 @@ mysql_send_columndef(DCB *dcb, const char *name, int type, int len, uint8_t seqn
|
||||
*ptr++ = (len >> 8) & 0xff;
|
||||
*ptr++ = (len >> 16) & 0xff;
|
||||
*ptr++ = (len >> 24) & 0xff;
|
||||
*ptr++ = type;
|
||||
*ptr++ = TABLE_COL_TYPE_VARCHAR;
|
||||
*ptr++ = 0x81; // Two bytes of flags
|
||||
if (type == 0xfd)
|
||||
{
|
||||
*ptr++ = 0x1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
*ptr++ = 0x00;
|
||||
}
|
||||
*ptr++ = 0x00;
|
||||
*ptr++ = 0;
|
||||
*ptr++ = 0;
|
||||
*ptr++ = 0;
|
||||
return dcb->func.write(dcb, pkt);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send an EOF packet in a response packet sequence.
|
||||
*
|
||||
@ -359,17 +108,16 @@ mysql_send_columndef(DCB *dcb, const char *name, int type, int len, uint8_t seqn
|
||||
* @param seqno The sequence number of the EOF packet
|
||||
* @return Non-zero on success
|
||||
*/
|
||||
static int
|
||||
mysql_send_eof(DCB *dcb, int seqno)
|
||||
static int mysql_send_eof(DCB *dcb, int seqno)
|
||||
{
|
||||
GWBUF *pkt;
|
||||
uint8_t *ptr;
|
||||
GWBUF* pkt = gwbuf_alloc(9);
|
||||
|
||||
if ((pkt = gwbuf_alloc(9)) == NULL)
|
||||
if (pkt == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
ptr = GWBUF_DATA(pkt);
|
||||
|
||||
uint8_t* ptr = GWBUF_DATA(pkt);
|
||||
*ptr++ = 0x05;
|
||||
*ptr++ = 0x00;
|
||||
*ptr++ = 0x00;
|
||||
@ -382,8 +130,6 @@ mysql_send_eof(DCB *dcb, int seqno)
|
||||
return dcb->func.write(dcb, pkt);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Send a row packet in a response packet sequence.
|
||||
*
|
||||
@ -392,129 +138,72 @@ mysql_send_eof(DCB *dcb, int seqno)
|
||||
* @param seqno The sequence number of the EOF packet
|
||||
* @return Non-zero on success
|
||||
*/
|
||||
static int
|
||||
mysql_send_row(DCB *dcb, RESULT_ROW *row, int seqno)
|
||||
static int mysql_send_row(DCB *dcb, const std::vector<std::string>& row, int seqno)
|
||||
{
|
||||
GWBUF *pkt;
|
||||
int i, len = 4;
|
||||
uint8_t *ptr;
|
||||
|
||||
for (i = 0; i < row->n_cols; i++)
|
||||
auto acc = [](int l, const std::string& s)
|
||||
{
|
||||
if (row->cols[i])
|
||||
{
|
||||
len += strlen(row->cols[i]);
|
||||
}
|
||||
len++;
|
||||
}
|
||||
return l + s.length() + 1;
|
||||
};
|
||||
|
||||
if ((pkt = gwbuf_alloc(len)) == NULL)
|
||||
int len = std::accumulate(row.begin(), row.end(), MYSQL_HEADER_LEN, acc);
|
||||
|
||||
GWBUF* pkt = gwbuf_alloc(len);
|
||||
|
||||
if (pkt == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
ptr = GWBUF_DATA(pkt);
|
||||
len -= 4;
|
||||
|
||||
uint8_t* ptr = GWBUF_DATA(pkt);
|
||||
len -= MYSQL_HEADER_LEN;
|
||||
*ptr++ = len & 0xff;
|
||||
*ptr++ = (len >> 8) & 0xff;
|
||||
*ptr++ = (len >> 16) & 0xff;
|
||||
*ptr++ = seqno;
|
||||
for (i = 0; i < row->n_cols; i++)
|
||||
|
||||
for (auto&& a : row)
|
||||
{
|
||||
if (row->cols[i])
|
||||
{
|
||||
len = strlen(row->cols[i]);
|
||||
*ptr++ = len;
|
||||
memcpy(ptr, row->cols[i], len);
|
||||
ptr += len;
|
||||
}
|
||||
else
|
||||
{
|
||||
*ptr++ = 0; // NULL column
|
||||
}
|
||||
*ptr++ = a.length();
|
||||
memcpy(ptr, a.c_str(), a.length());
|
||||
ptr += a.length();
|
||||
}
|
||||
|
||||
return dcb->func.write(dcb, pkt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the string only contains numerics
|
||||
*
|
||||
* @param value String to test
|
||||
* @return Non-zero if the string is made of of numeric values
|
||||
*/
|
||||
static int
|
||||
value_is_numeric(const char *value)
|
||||
ResultSet::ResultSet(std::initializer_list<std::string> names):
|
||||
m_columns(names)
|
||||
{
|
||||
int rval = 0;
|
||||
|
||||
if (*value)
|
||||
{
|
||||
rval = 1;
|
||||
while (*value)
|
||||
{
|
||||
if (!isdigit(*value))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
value++;
|
||||
}
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream a result set encoding it as a JSON object
|
||||
* Each row is retrieved by calling the function passed in the
|
||||
* argument list.
|
||||
*
|
||||
* @param set The result set to stream
|
||||
* @param dcb The connection to stream the result set to
|
||||
*/
|
||||
void
|
||||
resultset_stream_json(RESULTSET *set, DCB *dcb)
|
||||
std::unique_ptr<ResultSet> ResultSet::create(std::initializer_list<std::string> names)
|
||||
{
|
||||
RESULT_COLUMN *col;
|
||||
RESULT_ROW *row;
|
||||
int rowno = 0;
|
||||
|
||||
dcb_printf(dcb, "[ ");
|
||||
while ((row = (*set->fetchrow)(set, set->userdata)) != NULL)
|
||||
{
|
||||
int i = 0;
|
||||
if (rowno++ > 0)
|
||||
{
|
||||
dcb_printf(dcb, ",\n");
|
||||
}
|
||||
dcb_printf(dcb, "{ ");
|
||||
col = set->column;
|
||||
while (col)
|
||||
{
|
||||
dcb_printf(dcb, "\"%s\" : ", col->name);
|
||||
if (row->cols[i])
|
||||
{
|
||||
if (value_is_numeric(row->cols[i]))
|
||||
{
|
||||
dcb_printf(dcb, "%s", row->cols[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
dcb_printf(dcb, "\"%s\"", row->cols[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dcb_printf(dcb, "null");
|
||||
}
|
||||
i++;
|
||||
col = col->next;
|
||||
if (col)
|
||||
{
|
||||
dcb_printf(dcb, ", ");
|
||||
}
|
||||
}
|
||||
resultset_free_row(row);
|
||||
dcb_printf(dcb, "}");
|
||||
}
|
||||
dcb_printf(dcb, "]\n");
|
||||
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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user