Address only specific type of columns when masking
Only columns that are of string kind are now subject to masking. With a textual resultset it would be possible to mask anything the same way, but with a binary resultset it is not. Thus, so that the result is not dependent upon whether the resultset happens to be textual or not, only string fields are accessed. Documentation to be updated in a separate change. The utility classes have now also been rearranged somewhat.
This commit is contained in:
parent
10ba67be3c
commit
3acb43e2ed
@ -197,23 +197,23 @@ void MaskingFilterSession::handle_row(GWBUF* pPacket)
|
||||
{
|
||||
case MYSQL_COM_QUERY:
|
||||
{
|
||||
ComQueryResponse::Row row(response);
|
||||
ComQueryResponse::TextResultsetRow row(response, m_res.types());
|
||||
|
||||
ComQueryResponse::Row::iterator i = row.begin();
|
||||
ComQueryResponse::TextResultsetRow::iterator i = row.begin();
|
||||
while (i != row.end())
|
||||
{
|
||||
const MaskingRules::Rule* pRule = m_res.get_rule();
|
||||
|
||||
if (pRule)
|
||||
{
|
||||
LEncString s = *i;
|
||||
ComQueryResponse::TextResultsetRow::Value value = *i;
|
||||
|
||||
if (!s.is_null())
|
||||
if (value.is_string())
|
||||
{
|
||||
LEncString s = value.as_string();
|
||||
pRule->rewrite(s);
|
||||
}
|
||||
|
||||
MXS_NOTICE("String: %s", (*i).to_string().c_str());
|
||||
}
|
||||
++i;
|
||||
}
|
||||
@ -222,16 +222,16 @@ void MaskingFilterSession::handle_row(GWBUF* pPacket)
|
||||
|
||||
case MYSQL_COM_STMT_EXECUTE:
|
||||
{
|
||||
ComQueryResponse::BinaryRow row(response, m_res.types());
|
||||
ComQueryResponse::BinaryResultsetRow row(response, m_res.types());
|
||||
|
||||
ComQueryResponse::BinaryRow::iterator i = row.begin();
|
||||
ComQueryResponse::BinaryResultsetRow::iterator i = row.begin();
|
||||
while (i != row.end())
|
||||
{
|
||||
const MaskingRules::Rule* pRule = m_res.get_rule();
|
||||
|
||||
if (pRule)
|
||||
{
|
||||
ComQueryResponse::BinaryRow::Value value = *i;
|
||||
ComQueryResponse::BinaryResultsetRow::Value value = *i;
|
||||
|
||||
if (value.is_string())
|
||||
{
|
||||
|
@ -473,7 +473,11 @@ inline LEncString::iterator operator - (const LEncString::iterator& it, ptrdiff_
|
||||
return it;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @class ComPacket
|
||||
*
|
||||
* Base-class of all packet classes.
|
||||
*/
|
||||
class ComPacket
|
||||
{
|
||||
public:
|
||||
@ -490,45 +494,51 @@ public:
|
||||
protected:
|
||||
ComPacket(GWBUF* pPacket)
|
||||
: m_pPacket(pPacket)
|
||||
, m_pI(GWBUF_DATA(pPacket))
|
||||
, m_packet_len(MYSQL_GET_PAYLOAD_LEN(m_pI))
|
||||
, m_packet_no(MYSQL_GET_PACKET_NO(m_pI))
|
||||
, m_pData(GWBUF_DATA(pPacket))
|
||||
, m_packet_len(MYSQL_GET_PAYLOAD_LEN(m_pData))
|
||||
, m_packet_no(MYSQL_GET_PACKET_NO(m_pData))
|
||||
{
|
||||
m_pI += MYSQL_HEADER_LEN;
|
||||
m_pData += MYSQL_HEADER_LEN;
|
||||
}
|
||||
|
||||
ComPacket(const ComPacket& packet)
|
||||
: m_pPacket(packet.m_pPacket)
|
||||
, m_pI(GWBUF_DATA(m_pPacket))
|
||||
, m_pData(GWBUF_DATA(m_pPacket))
|
||||
, m_packet_len(packet.m_packet_len)
|
||||
, m_packet_no(packet.m_packet_no)
|
||||
{
|
||||
m_pI += MYSQL_HEADER_LEN;
|
||||
m_pData += MYSQL_HEADER_LEN;
|
||||
}
|
||||
|
||||
GWBUF* m_pPacket;
|
||||
uint8_t* m_pI;
|
||||
uint8_t* m_pData;
|
||||
|
||||
private:
|
||||
uint32_t m_packet_len;
|
||||
uint8_t m_packet_no;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @class ComResponse
|
||||
*
|
||||
* Base-class of all response packet classes.
|
||||
*/
|
||||
class ComResponse : public ComPacket
|
||||
{
|
||||
public:
|
||||
ComResponse(GWBUF* pPacket)
|
||||
: ComPacket(pPacket)
|
||||
, m_type(*m_pI)
|
||||
, m_type(*m_pData)
|
||||
{
|
||||
++m_pI;
|
||||
++m_pData;
|
||||
}
|
||||
|
||||
ComResponse(const ComResponse& packet)
|
||||
: ComPacket(packet)
|
||||
, m_type(packet.m_type)
|
||||
{
|
||||
++m_pI;
|
||||
++m_pData;
|
||||
}
|
||||
|
||||
uint8_t type() const
|
||||
@ -555,14 +565,19 @@ protected:
|
||||
uint8_t m_type;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class ComRequest
|
||||
*
|
||||
* Base-class of all request packet classes.
|
||||
*/
|
||||
class ComRequest : public ComPacket
|
||||
{
|
||||
public:
|
||||
ComRequest(GWBUF* pPacket)
|
||||
: ComPacket(pPacket)
|
||||
, m_command(*m_pI)
|
||||
, m_command(*m_pData)
|
||||
{
|
||||
++m_pI;
|
||||
++m_pData;
|
||||
}
|
||||
|
||||
uint8_t command() const { return m_command; }
|
||||
@ -571,33 +586,41 @@ protected:
|
||||
uint8_t m_command;
|
||||
};
|
||||
|
||||
class ComQueryResponseColumnDef : public ComPacket
|
||||
/**
|
||||
* @class CQRColumnDef
|
||||
*
|
||||
* The column definition of the response of a @c ComQuery.
|
||||
*
|
||||
* @attention The name should not be used as such, but always using the
|
||||
* typedef @c ComQueryResponse::ColumnDef.
|
||||
*/
|
||||
class CQRColumnDef : public ComPacket
|
||||
{
|
||||
public:
|
||||
ComQueryResponseColumnDef(GWBUF* pPacket)
|
||||
CQRColumnDef(GWBUF* pPacket)
|
||||
: ComPacket(pPacket)
|
||||
, m_catalog(&m_pI)
|
||||
, m_schema(&m_pI)
|
||||
, m_table(&m_pI)
|
||||
, m_org_table(&m_pI)
|
||||
, m_name(&m_pI)
|
||||
, m_org_name(&m_pI)
|
||||
, m_length_fixed_fields(&m_pI)
|
||||
, m_catalog(&m_pData)
|
||||
, m_schema(&m_pData)
|
||||
, m_table(&m_pData)
|
||||
, m_org_table(&m_pData)
|
||||
, m_name(&m_pData)
|
||||
, m_org_name(&m_pData)
|
||||
, m_length_fixed_fields(&m_pData)
|
||||
{
|
||||
m_character_set = *reinterpret_cast<const uint16_t*>(m_pI);
|
||||
m_pI += 2;
|
||||
m_character_set = *reinterpret_cast<const uint16_t*>(m_pData);
|
||||
m_pData += 2;
|
||||
|
||||
m_column_length = *reinterpret_cast<const uint32_t*>(m_pI);
|
||||
m_pI += 4;
|
||||
m_column_length = *reinterpret_cast<const uint32_t*>(m_pData);
|
||||
m_pData += 4;
|
||||
|
||||
m_type = static_cast<enum_field_types>(*m_pI);
|
||||
m_pI += 1;
|
||||
m_type = static_cast<enum_field_types>(*m_pData);
|
||||
m_pData += 1;
|
||||
|
||||
m_flags = *reinterpret_cast<const uint16_t*>(m_pI);
|
||||
m_pI += 2;
|
||||
m_flags = *reinterpret_cast<const uint16_t*>(m_pData);
|
||||
m_pData += 2;
|
||||
|
||||
m_decimals = *m_pI;
|
||||
m_pI += 1;
|
||||
m_decimals = *m_pData;
|
||||
m_pData += 1;
|
||||
}
|
||||
|
||||
const LEncString& catalog() const { return m_catalog; }
|
||||
@ -641,350 +664,402 @@ private:
|
||||
uint8_t m_decimals;
|
||||
};
|
||||
|
||||
class ComQueryResponseRow : public ComPacket
|
||||
/**
|
||||
* @class CQRResultsetValue
|
||||
*
|
||||
* An instance of this class represents a value in a resultset row. As this
|
||||
* currently is for the purpose of the masking filter, it effectively is useful
|
||||
* for accessing NULL and string values.
|
||||
*
|
||||
* @attention The name should not be used as such, but instead either
|
||||
* @c ComQueryResponse::TextResultsetRow::Value or
|
||||
* @c ComQueryResponse::TextResultsetRow::Value.
|
||||
*/
|
||||
class CQRResultsetValue
|
||||
{
|
||||
public:
|
||||
class iterator : public std::iterator<std::forward_iterator_tag,
|
||||
LEncString,
|
||||
std::ptrdiff_t,
|
||||
LEncString*,
|
||||
LEncString>
|
||||
{
|
||||
public:
|
||||
iterator(uint8_t* pI = NULL)
|
||||
: m_pI(pI)
|
||||
{}
|
||||
|
||||
iterator& operator++()
|
||||
{
|
||||
LEncString s(&m_pI);
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator++(int)
|
||||
{
|
||||
iterator rv(*this);
|
||||
++(*this);
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool operator == (const iterator& rhs) const
|
||||
{
|
||||
return m_pI == rhs.m_pI;
|
||||
}
|
||||
|
||||
bool operator != (const iterator& rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
reference operator*()
|
||||
{
|
||||
return LEncString(m_pI);
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t* m_pI;
|
||||
};
|
||||
|
||||
ComQueryResponseRow(GWBUF* pPacket)
|
||||
: ComPacket(pPacket)
|
||||
CQRResultsetValue()
|
||||
: m_type(MYSQL_TYPE_NULL)
|
||||
, m_pData(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
ComQueryResponseRow(const ComResponse& packet)
|
||||
: ComPacket(packet)
|
||||
CQRResultsetValue(enum_field_types type, uint8_t* pData)
|
||||
: m_type(type)
|
||||
, m_pData(pData)
|
||||
{
|
||||
}
|
||||
|
||||
iterator begin()
|
||||
LEncString as_string()
|
||||
{
|
||||
return iterator(m_pI);
|
||||
ss_dassert(is_string());
|
||||
return LEncString(m_pData);
|
||||
}
|
||||
|
||||
iterator end()
|
||||
bool is_null() const
|
||||
{
|
||||
uint8_t* pEnd = GWBUF_DATA(m_pPacket) + GWBUF_LENGTH(m_pPacket);
|
||||
return iterator(pEnd);
|
||||
return m_type == MYSQL_TYPE_NULL;
|
||||
}
|
||||
|
||||
bool is_string() const
|
||||
{
|
||||
return is_string(m_type);
|
||||
}
|
||||
|
||||
static bool is_string(enum_field_types type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case MYSQL_TYPE_BLOB:
|
||||
case MYSQL_TYPE_LONG_BLOB:
|
||||
case MYSQL_TYPE_MEDIUM_BLOB:
|
||||
case MYSQL_TYPE_STRING:
|
||||
case MYSQL_TYPE_TINY_BLOB:
|
||||
case MYSQL_TYPE_VARCHAR:
|
||||
case MYSQL_TYPE_VAR_STRING:
|
||||
return true;
|
||||
|
||||
// These, although returned as length-encoded strings, also in the case of
|
||||
// a binary resultset row, are not are not considered to be strings from the
|
||||
// perspective of masking.
|
||||
case MYSQL_TYPE_BIT:
|
||||
case MYSQL_TYPE_DECIMAL:
|
||||
case MYSQL_TYPE_ENUM:
|
||||
case MYSQL_TYPE_GEOMETRY:
|
||||
case MYSQL_TYPE_NEWDECIMAL:
|
||||
case MYSQL_TYPE_SET:
|
||||
return false;
|
||||
|
||||
default:
|
||||
// Nothing else is considered to be strings even though, in the case of
|
||||
// a textual resultset, that's what they all are.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
enum_field_types m_type;
|
||||
uint8_t* m_pData;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class CQRTextResultsetValue
|
||||
*
|
||||
* An instance of this class represents a value in a textual resultset row.
|
||||
*
|
||||
* @attention The name should not be used as such, but always using the
|
||||
* typedef @c ComQueryResponse::TextResultsetRow::Value.
|
||||
*/
|
||||
class CQRTextResultsetValue : public CQRResultsetValue
|
||||
{
|
||||
public:
|
||||
CQRTextResultsetValue(enum_field_types type, uint8_t* pData)
|
||||
: CQRResultsetValue(type, pData)
|
||||
{
|
||||
if (*pData == 0xfb)
|
||||
{
|
||||
m_type = MYSQL_TYPE_NULL;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ComQueryResponseBinaryRow : public ComPacket
|
||||
/**
|
||||
* @class CQRBinaryResultsetValue
|
||||
*
|
||||
* An instance of this class represents a value in a binary resultset row.
|
||||
*
|
||||
* @attention The name should not be used as such, but always using the
|
||||
* typedef @c ComQueryResponse::BinaryResultsetRow::Value.
|
||||
*/
|
||||
typedef CQRResultsetValue CQRBinaryResultsetValue;
|
||||
|
||||
/**
|
||||
* @class CQRTextResultsetRowIterator
|
||||
*
|
||||
* An STL compatible iterator that iterates over the values in a textual resultset.
|
||||
*
|
||||
* @attention The name should not be used as such, but always using the
|
||||
* typedef @c ComQueryResponse::TextResultset::iterator.
|
||||
*/
|
||||
class CQRTextResultsetRowIterator : public std::iterator<std::forward_iterator_tag,
|
||||
CQRTextResultsetValue,
|
||||
std::ptrdiff_t,
|
||||
CQRTextResultsetValue*,
|
||||
CQRTextResultsetValue>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* An instance of Value represents a value in a binary resultset.
|
||||
*/
|
||||
class Value
|
||||
typedef CQRTextResultsetValue Value;
|
||||
|
||||
CQRTextResultsetRowIterator(uint8_t* pData, const std::vector<enum_field_types>& types)
|
||||
: m_pData(pData)
|
||||
, m_iTypes(types.begin())
|
||||
{}
|
||||
|
||||
CQRTextResultsetRowIterator(uint8_t* pData)
|
||||
: m_pData(pData)
|
||||
{}
|
||||
|
||||
CQRTextResultsetRowIterator& operator++()
|
||||
{
|
||||
public:
|
||||
Value()
|
||||
: m_type(MYSQL_TYPE_NULL)
|
||||
, m_pData(NULL)
|
||||
{
|
||||
}
|
||||
// In the textual protocol, every value is a length encoded string.
|
||||
LEncString s(&m_pData);
|
||||
++m_iTypes;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value(enum_field_types type, uint8_t* pData)
|
||||
: m_type(type)
|
||||
, m_pData(pData)
|
||||
{
|
||||
}
|
||||
CQRTextResultsetRowIterator operator++(int)
|
||||
{
|
||||
CQRTextResultsetRowIterator rv(*this);
|
||||
++(*this);
|
||||
return rv;
|
||||
}
|
||||
|
||||
enum_field_types type() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
bool operator == (const CQRTextResultsetRowIterator& rhs) const
|
||||
{
|
||||
return m_pData == rhs.m_pData;
|
||||
}
|
||||
|
||||
LEncString as_string()
|
||||
{
|
||||
ss_dassert(is_string(m_type));
|
||||
return LEncString(m_pData);
|
||||
}
|
||||
bool operator != (const CQRTextResultsetRowIterator& rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
bool is_string() const
|
||||
{
|
||||
return is_string(m_type);
|
||||
}
|
||||
CQRTextResultsetValue operator*()
|
||||
{
|
||||
return Value(*m_iTypes, m_pData);
|
||||
}
|
||||
|
||||
static bool is_string(enum_field_types type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case MYSQL_TYPE_STRING:
|
||||
case MYSQL_TYPE_VARCHAR:
|
||||
case MYSQL_TYPE_VAR_STRING:
|
||||
return true;
|
||||
private:
|
||||
uint8_t* m_pData;
|
||||
std::vector<enum_field_types>::const_iterator m_iTypes;
|
||||
};
|
||||
|
||||
// These, although returned as length-encoded strings are not considered
|
||||
// to be strings from the perspective of masking.
|
||||
case MYSQL_TYPE_BIT:
|
||||
case MYSQL_TYPE_BLOB:
|
||||
case MYSQL_TYPE_DECIMAL:
|
||||
case MYSQL_TYPE_ENUM:
|
||||
case MYSQL_TYPE_GEOMETRY:
|
||||
case MYSQL_TYPE_LONG_BLOB:
|
||||
case MYSQL_TYPE_MEDIUM_BLOB:
|
||||
case MYSQL_TYPE_NEWDECIMAL:
|
||||
case MYSQL_TYPE_SET:
|
||||
case MYSQL_TYPE_TINY_BLOB:
|
||||
return false;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
enum_field_types m_type;
|
||||
uint8_t* m_pData;
|
||||
};
|
||||
/**
|
||||
* @class CQRBinaryResultsetRowIterator
|
||||
*
|
||||
* An STL compatible iterator that iterates over the values in a binary resultset.
|
||||
*
|
||||
* @attention The name should not be used as such, but always using the
|
||||
* typedef @c ComQueryResponse::BinaryResultset::iterator.
|
||||
*/
|
||||
class CQRBinaryResultsetRowIterator : public std::iterator<std::forward_iterator_tag,
|
||||
CQRBinaryResultsetValue,
|
||||
std::ptrdiff_t,
|
||||
CQRBinaryResultsetValue*,
|
||||
CQRBinaryResultsetValue>
|
||||
{
|
||||
public:
|
||||
typedef CQRBinaryResultsetValue Value;
|
||||
|
||||
/**
|
||||
* iterator is an iterator to values in a binary resultset.
|
||||
* A bit_iterator is an iterator to bits in an array of bytes.
|
||||
*
|
||||
* Specifically, it is capable of iterating across the NULL bitmask of
|
||||
* a binary resultset.
|
||||
*/
|
||||
class iterator : public std::iterator<std::forward_iterator_tag,
|
||||
Value,
|
||||
std::ptrdiff_t,
|
||||
Value*,
|
||||
Value>
|
||||
class bit_iterator
|
||||
{
|
||||
public:
|
||||
bit_iterator(uint8_t* pData = 0)
|
||||
: m_pData(pData)
|
||||
, m_mask(1 << 2) // The two first bits are not used.
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* A bit_iterator is an iterator to bits in an array of bytes.
|
||||
*
|
||||
* Specifically, it is capable of iterating across the NULL bitmask of
|
||||
* a binary resultset.
|
||||
* @return True, if the current bit is on. That is, if the corresponding
|
||||
* column value is NULL.
|
||||
*/
|
||||
class bit_iterator
|
||||
bool operator * () const
|
||||
{
|
||||
public:
|
||||
bit_iterator(uint8_t* pData = 0)
|
||||
: m_pData(pData)
|
||||
, m_mask(1 << 2) // The two first bits are not used.
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True, if the current bit is on. That is, if the corresponding
|
||||
* column value is NULL.
|
||||
*/
|
||||
bool operator * () const
|
||||
{
|
||||
return (*m_pData & m_mask) ? true : false;
|
||||
}
|
||||
|
||||
bit_iterator& operator ++ ()
|
||||
{
|
||||
m_mask <<= 1; // Move to the next bit.
|
||||
if (m_mask == 0)
|
||||
{
|
||||
// We moved past the byte, so advance to next byte and the first bit of that.
|
||||
++m_pData;
|
||||
m_mask = 1;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bit_iterator operator ++ (int)
|
||||
{
|
||||
bit_iterator rv(*this);
|
||||
++(*this);
|
||||
return rv;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t* m_pData; /*< Pointer to the NULL bitmap of a binary resultset row. */
|
||||
uint8_t m_mask; /*< Mask representing the current bit of the current byte. */
|
||||
};
|
||||
|
||||
iterator(uint8_t* pData, const std::vector<enum_field_types>& types)
|
||||
: m_pData(pData)
|
||||
, m_iTypes(types.begin())
|
||||
, m_iNulls(pData + 1)
|
||||
{
|
||||
ss_dassert(*m_pData == 0);
|
||||
++m_pData;
|
||||
|
||||
// See https://dev.mysql.com/doc/internals/en/binary-protocol-resultset-row.html
|
||||
size_t nNull_bytes = (types.size() + 7 + 2) / 8;
|
||||
|
||||
m_pData += nNull_bytes;
|
||||
return (*m_pData & m_mask) ? true : false;
|
||||
}
|
||||
|
||||
iterator(uint8_t* pData)
|
||||
: m_pData(pData)
|
||||
bit_iterator& operator ++ ()
|
||||
{
|
||||
}
|
||||
|
||||
iterator& operator++()
|
||||
{
|
||||
// See https://dev.mysql.com/doc/internals/en/binary-protocol-value.html
|
||||
switch (*m_iTypes)
|
||||
m_mask <<= 1; // Move to the next bit.
|
||||
if (m_mask == 0)
|
||||
{
|
||||
case MYSQL_TYPE_BIT:
|
||||
case MYSQL_TYPE_BLOB:
|
||||
case MYSQL_TYPE_DECIMAL:
|
||||
case MYSQL_TYPE_ENUM:
|
||||
case MYSQL_TYPE_GEOMETRY:
|
||||
case MYSQL_TYPE_LONG_BLOB:
|
||||
case MYSQL_TYPE_MEDIUM_BLOB:
|
||||
case MYSQL_TYPE_NEWDATE:
|
||||
case MYSQL_TYPE_NEWDECIMAL:
|
||||
case MYSQL_TYPE_SET:
|
||||
case MYSQL_TYPE_STRING:
|
||||
case MYSQL_TYPE_TINY_BLOB:
|
||||
case MYSQL_TYPE_VARCHAR:
|
||||
case MYSQL_TYPE_VAR_STRING:
|
||||
{
|
||||
LEncString s(&m_pData); // Advance m_pData to the byte following the string.
|
||||
}
|
||||
break;
|
||||
|
||||
case MYSQL_TYPE_LONGLONG:
|
||||
m_pData += 8;
|
||||
break;
|
||||
|
||||
case MYSQL_TYPE_LONG:
|
||||
case MYSQL_TYPE_INT24:
|
||||
m_pData += 4;
|
||||
break;
|
||||
|
||||
case MYSQL_TYPE_SHORT:
|
||||
case MYSQL_TYPE_YEAR:
|
||||
m_pData += 2;
|
||||
break;
|
||||
|
||||
case MYSQL_TYPE_TINY:
|
||||
m_pData += 1;
|
||||
break;
|
||||
|
||||
case MYSQL_TYPE_DOUBLE:
|
||||
m_pData += 8;
|
||||
break;
|
||||
|
||||
case MYSQL_TYPE_FLOAT:
|
||||
m_pData += 4;
|
||||
break;
|
||||
|
||||
case MYSQL_TYPE_DATE:
|
||||
case MYSQL_TYPE_DATETIME:
|
||||
case MYSQL_TYPE_TIMESTAMP:
|
||||
{
|
||||
// A byte specifying the length, followed by that many bytes.
|
||||
// Either 0, 4, 7 or 11.
|
||||
uint8_t len = *m_pData++;
|
||||
m_pData += len;
|
||||
}
|
||||
break;
|
||||
|
||||
case MYSQL_TYPE_TIME:
|
||||
{
|
||||
// A byte specifying the length, followed by that many bytes.
|
||||
// Either 0, 8 or 12.
|
||||
uint8_t len = *m_pData++;
|
||||
m_pData += len;
|
||||
}
|
||||
break;
|
||||
|
||||
case MYSQL_TYPE_NULL:
|
||||
break;
|
||||
|
||||
case MAX_NO_FIELD_TYPES:
|
||||
ss_dassert(!true);
|
||||
break;
|
||||
// We moved past the byte, so advance to next byte and the first bit of that.
|
||||
++m_pData;
|
||||
m_mask = 1;
|
||||
}
|
||||
|
||||
++m_iNulls;
|
||||
++m_iTypes;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator++(int)
|
||||
bit_iterator operator ++ (int)
|
||||
{
|
||||
iterator rv(*this);
|
||||
bit_iterator rv(*this);
|
||||
++(*this);
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool operator == (const iterator& rhs) const
|
||||
{
|
||||
return m_pData == rhs.m_pData;
|
||||
}
|
||||
|
||||
bool operator != (const iterator& rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
reference operator*()
|
||||
{
|
||||
if (*m_iNulls)
|
||||
{
|
||||
return Value();
|
||||
}
|
||||
else
|
||||
{
|
||||
return Value(*m_iTypes, m_pData);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t* m_pData;
|
||||
std::vector<enum_field_types>::const_iterator m_iTypes;
|
||||
bit_iterator m_iNulls;
|
||||
uint8_t* m_pData; /*< Pointer to the NULL bitmap of a binary resultset row. */
|
||||
uint8_t m_mask; /*< Mask representing the current bit of the current byte. */
|
||||
};
|
||||
|
||||
ComQueryResponseBinaryRow(GWBUF* pPacket,
|
||||
const std::vector<enum_field_types>& types)
|
||||
CQRBinaryResultsetRowIterator(uint8_t* pData, const std::vector<enum_field_types>& types)
|
||||
: m_pData(pData)
|
||||
, m_iTypes(types.begin())
|
||||
, m_iNulls(pData + 1)
|
||||
{
|
||||
ss_dassert(*m_pData == 0);
|
||||
++m_pData;
|
||||
|
||||
// See https://dev.mysql.com/doc/internals/en/binary-protocol-resultset-row.html
|
||||
size_t nNull_bytes = (types.size() + 7 + 2) / 8;
|
||||
|
||||
m_pData += nNull_bytes;
|
||||
}
|
||||
|
||||
CQRBinaryResultsetRowIterator(uint8_t* pData)
|
||||
: m_pData(pData)
|
||||
{
|
||||
}
|
||||
|
||||
CQRBinaryResultsetRowIterator& operator++()
|
||||
{
|
||||
// See https://dev.mysql.com/doc/internals/en/binary-protocol-value.html
|
||||
switch (*m_iTypes)
|
||||
{
|
||||
case MYSQL_TYPE_BIT:
|
||||
case MYSQL_TYPE_BLOB:
|
||||
case MYSQL_TYPE_DECIMAL:
|
||||
case MYSQL_TYPE_ENUM:
|
||||
case MYSQL_TYPE_GEOMETRY:
|
||||
case MYSQL_TYPE_LONG_BLOB:
|
||||
case MYSQL_TYPE_MEDIUM_BLOB:
|
||||
case MYSQL_TYPE_NEWDATE:
|
||||
case MYSQL_TYPE_NEWDECIMAL:
|
||||
case MYSQL_TYPE_SET:
|
||||
case MYSQL_TYPE_STRING:
|
||||
case MYSQL_TYPE_TINY_BLOB:
|
||||
case MYSQL_TYPE_VARCHAR:
|
||||
case MYSQL_TYPE_VAR_STRING:
|
||||
{
|
||||
LEncString s(&m_pData); // Advance m_pData to the byte following the string.
|
||||
}
|
||||
break;
|
||||
|
||||
case MYSQL_TYPE_LONGLONG:
|
||||
m_pData += 8;
|
||||
break;
|
||||
|
||||
case MYSQL_TYPE_LONG:
|
||||
case MYSQL_TYPE_INT24:
|
||||
m_pData += 4;
|
||||
break;
|
||||
|
||||
case MYSQL_TYPE_SHORT:
|
||||
case MYSQL_TYPE_YEAR:
|
||||
m_pData += 2;
|
||||
break;
|
||||
|
||||
case MYSQL_TYPE_TINY:
|
||||
m_pData += 1;
|
||||
break;
|
||||
|
||||
case MYSQL_TYPE_DOUBLE:
|
||||
m_pData += 8;
|
||||
break;
|
||||
|
||||
case MYSQL_TYPE_FLOAT:
|
||||
m_pData += 4;
|
||||
break;
|
||||
|
||||
case MYSQL_TYPE_DATE:
|
||||
case MYSQL_TYPE_DATETIME:
|
||||
case MYSQL_TYPE_TIMESTAMP:
|
||||
{
|
||||
// A byte specifying the length, followed by that many bytes.
|
||||
// Either 0, 4, 7 or 11.
|
||||
uint8_t len = *m_pData++;
|
||||
m_pData += len;
|
||||
}
|
||||
break;
|
||||
|
||||
case MYSQL_TYPE_TIME:
|
||||
{
|
||||
// A byte specifying the length, followed by that many bytes.
|
||||
// Either 0, 8 or 12.
|
||||
uint8_t len = *m_pData++;
|
||||
m_pData += len;
|
||||
}
|
||||
break;
|
||||
|
||||
case MYSQL_TYPE_NULL:
|
||||
break;
|
||||
|
||||
case MAX_NO_FIELD_TYPES:
|
||||
ss_dassert(!true);
|
||||
break;
|
||||
}
|
||||
|
||||
++m_iNulls;
|
||||
++m_iTypes;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CQRBinaryResultsetRowIterator operator++(int)
|
||||
{
|
||||
CQRBinaryResultsetRowIterator rv(*this);
|
||||
++(*this);
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool operator == (const CQRBinaryResultsetRowIterator& rhs) const
|
||||
{
|
||||
return m_pData == rhs.m_pData;
|
||||
}
|
||||
|
||||
bool operator != (const CQRBinaryResultsetRowIterator& rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
reference operator*()
|
||||
{
|
||||
if (*m_iNulls)
|
||||
{
|
||||
return Value();
|
||||
}
|
||||
else
|
||||
{
|
||||
return Value(*m_iTypes, m_pData);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t* m_pData;
|
||||
std::vector<enum_field_types>::const_iterator m_iTypes;
|
||||
bit_iterator m_iNulls;
|
||||
};
|
||||
|
||||
/**
|
||||
* @template CQRResultsetRow
|
||||
*
|
||||
* A template that when instantiated either represents a textual or a
|
||||
* binary resultset row.
|
||||
*/
|
||||
template<class Iterator>
|
||||
class CQRResultsetRow : public ComPacket
|
||||
{
|
||||
public:
|
||||
typedef typename Iterator::Value Value;
|
||||
typedef Iterator iterator;
|
||||
|
||||
CQRResultsetRow(GWBUF* pPacket,
|
||||
const std::vector<enum_field_types>& types)
|
||||
: ComPacket(pPacket)
|
||||
, m_types(types)
|
||||
{
|
||||
}
|
||||
|
||||
ComQueryResponseBinaryRow(const ComResponse& packet,
|
||||
const std::vector<enum_field_types>& types)
|
||||
CQRResultsetRow(const ComResponse& packet,
|
||||
const std::vector<enum_field_types>& types)
|
||||
: ComPacket(packet)
|
||||
, m_types(types)
|
||||
{
|
||||
@ -992,7 +1067,7 @@ public:
|
||||
|
||||
iterator begin()
|
||||
{
|
||||
return iterator(m_pI, m_types);
|
||||
return iterator(m_pData, m_types);
|
||||
}
|
||||
|
||||
iterator end()
|
||||
@ -1005,22 +1080,41 @@ private:
|
||||
const std::vector<enum_field_types>& m_types;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class CQRTextResultsetRow
|
||||
*
|
||||
* An instance of this class represents a textual resultset row.
|
||||
*/
|
||||
typedef CQRResultsetRow<CQRTextResultsetRowIterator> CQRTextResultsetRow;
|
||||
|
||||
/**
|
||||
* @class CQRBinaryResultsetRow
|
||||
*
|
||||
* An instance of this class represents a binary resultset row.
|
||||
*/
|
||||
typedef CQRResultsetRow<CQRBinaryResultsetRowIterator> CQRBinaryResultsetRow;
|
||||
|
||||
/**
|
||||
* @class ComQueryResponse
|
||||
*
|
||||
* An instance of this class represents the response to a @c ComQuery.
|
||||
*/
|
||||
class ComQueryResponse : public ComPacket
|
||||
{
|
||||
public:
|
||||
typedef ComQueryResponseColumnDef ColumnDef;
|
||||
typedef ComQueryResponseRow Row;
|
||||
typedef ComQueryResponseBinaryRow BinaryRow;
|
||||
typedef CQRColumnDef ColumnDef;
|
||||
typedef CQRTextResultsetRow TextResultsetRow;
|
||||
typedef CQRBinaryResultsetRow BinaryResultsetRow;
|
||||
|
||||
ComQueryResponse(GWBUF* pPacket)
|
||||
: ComPacket(pPacket)
|
||||
, m_nFields(&m_pI)
|
||||
, m_nFields(&m_pData)
|
||||
{
|
||||
}
|
||||
|
||||
ComQueryResponse(const ComResponse& packet)
|
||||
: ComPacket(packet)
|
||||
, m_nFields(&m_pI)
|
||||
, m_nFields(&m_pData)
|
||||
{
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user