common/result.h (530 lines of code) (raw):
/*
* Copyright (c) 2015, 2024, Oracle and/or its affiliates.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2.0, as
* published by the Free Software Foundation.
*
* This program is designed to work with certain software (including
* but not limited to OpenSSL) that is licensed under separate terms, as
* designated in a particular file or component or in included license
* documentation. The authors of MySQL hereby grant you an additional
* permission to link the program and your derivative works with the
* separately licensed software that they have either included with
* the program or referenced in the documentation.
*
* Without limiting anything contained in the foregoing, this file,
* which is part of Connector/C++, is also subject to the
* Universal FOSS Exception, version 1.0, a copy of which can be found at
* https://oss.oracle.com/licenses/universal-foss-exception.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License, version 2.0, for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef MYSQLX_COMMON_RESULT_INT_H
#define MYSQLX_COMMON_RESULT_INT_H
#include "common.h"
#include "session.h"
#include "value.h"
#include <mysql/cdk.h>
#include <mysql/cdk/converters.h>
#include <expr_parser.h>
PUSH_SYS_WARNINGS
#include <queue>
POP_SYS_WARNINGS
namespace mysqlx {
namespace impl {
namespace common {
// TODO: Use std::variant when available
using cdk::foundation::variant;
/*
Handling result meta-data information
=====================================
Meta-data for result columns is provided by CDK cursor object which implements
cdk::Meta_data interface. This information is read from cursor into
an instance of Meta_data class in Result_impl::get_meta_data() method which
is called from Result_impl_base::next_result() method when meta-data
is eceived from the server. Created Meta_data instance is shared between
result object and Row instances representing individual rows fetched from
the result. Inside Row instance, meta-data is used to decode values from raw
bytes.
Textual meta-data information, such as column names, can be stored either as
a wide string or utf8 encoded. For that reason the Meta_data and Column_info
classes are in fact templates parametrized by class STR used to store string
data (either std::string or some wide string class).
Meta_data class contains a map from column positions to instances
of Column_info class. Each Column_info instance can store meta-data
information for a single column. The Column_info instances are created in
the Meta_data constructor which reads meta-data information from
cdk::Meta_data interface and adds Column_info objects to the map using add()
methods.
The CDK meta-data for a single column consists of:
- CDK type constant (cdk::Type_info)
- encoding format information (cdk::Format_info)
- additional column information (cdk::Column_info)
The first two items describe the type and encoding of the values that appear
in the result in the corresponding column. Additional column information
is mainly the column name etc.
Classes Format_descr<TTT> and Format_info are used to store type and encoding
format information for each column. For CDK type TTT, class Format_descr<TTT>
stores encoding format descriptor (instance of cdk::Format<TTT> class) and
encoder/decoder for the values (instance of cdk::Codec<TTT> class)
(Note: for some types TTT there is no codec or no format descriptor). Class
Format_info is a variant class that can store Format_descr<TTT> values
for different TTT.
Class Column_info extends Format_info with additional storage for
the cdk::Column_info data (column name etc).
Using meta-data to decode result values
---------------------------------------
Meta-data information stored in m_mdata member of Result_impl_base class
is used to interpret raw bytes returned by CDK in the reply to a query.
This interpretation is done by Row_impl class which takes (shared pointer
to) Meta_data instance as its constructor parameter and stores it in its
m_mdata member. Encoding format information for a given column is extracted
from this Meta_data instance in Row_impl::get() which then passes it
to Row_impl::conver_at() and eventually to the static Value::Access::mk()
functions which construct value from raw bytes using the format encoding
information.
Row_impl is actually a template parametrized by the exact VAL class used
to decode and store data received from the server. Normally this should be
the common::Value class, but a specialization can be used which handles
either data conversion or data storage in some special way. For example,
X DevAPI uses a specialization to handle storage of structured data such
as documents and arrays.
The common::Value::Access::mk() functions are defined in terms of convert()
function overloads defined in result.cc. They look at the encoding format
information and use the encoder instance inside Format_descr<> object to
convert raw bytes into a value of appropriate type.
*/
/*
Encapsulates CDK encoding format information and a raw bytes decoder
for values of given CDK type T. For some types either the decoder or the
format information are redundant and thus not stored inside Format_descr<T>.
*/
template <cdk::Type_info T>
class Format_descr
{
public:
cdk::Format<T> m_format;
cdk::Codec<T> m_codec;
Format_descr(const cdk::Format_info &fi)
: m_format(fi), m_codec(fi)
{}
};
/*
Format_descr<T> specializations for different types.
*/
template <>
struct Format_descr<cdk::TYPE_DOCUMENT>
{
cdk::Format<cdk::TYPE_DOCUMENT> m_format;
cdk::Codec<cdk::TYPE_DOCUMENT> m_codec;
Format_descr(const cdk::Format_info &fi)
: m_format(fi)
{}
};
/*
Note: we do not decode temporal values yet, thus there is
no codec in Format_descr class.
*/
template<>
struct Format_descr<cdk::TYPE_DATETIME>
{
cdk::Format<cdk::TYPE_DATETIME> m_format;
Format_descr(const cdk::Format_info &fi)
: m_format(fi)
{}
};
template <>
struct Format_descr<cdk::TYPE_BYTES>
{
cdk::Format<cdk::TYPE_BYTES> m_format;
Format_descr(const cdk::Format_info &fi)
: m_format(fi)
{}
};
/*
Note: For GEOMETRY and XML types we do not decode the values.
Also, CDK does not provide any encoding format information -
GEOMETRY uses some unspecified MySQL internal representation
format and XML format is well known.
*/
template<>
struct Format_descr<cdk::TYPE_GEOMETRY>
{
Format_descr(const cdk::Format_info &)
{}
};
template<>
struct Format_descr<cdk::TYPE_XML>
{
Format_descr(const cdk::Format_info &)
{}
};
/*
Structure Format_info holds information about the type
of a column (m_type) and about its encoding format in
Format_descr<T> structure. Since C++ type of Format_descr<T>
is different for each T, a variant object is used to store
the appropriate Format_descr<T> value.
*/
struct Format_info
{
typedef variant <
Format_descr<cdk::TYPE_STRING>,
Format_descr<cdk::TYPE_INTEGER>,
Format_descr<cdk::TYPE_FLOAT>,
Format_descr<cdk::TYPE_DOCUMENT>,
Format_descr<cdk::TYPE_BYTES>,
Format_descr<cdk::TYPE_DATETIME>,
Format_descr<cdk::TYPE_GEOMETRY>,
Format_descr<cdk::TYPE_XML>
> Format_info_storage;
cdk::Type_info m_type;
Format_info_storage m_fmt;
template <cdk::Type_info T>
explicit Format_info(const Format_descr<T> &fd)
: m_type(T), m_fmt(fd)
{}
explicit
Format_info(cdk::Type_info type, const Format_descr<cdk::TYPE_BYTES> &fmt)
: m_type(type), m_fmt(fmt)
{}
template <cdk::Type_info T>
Format_descr<T>& get() const
{
/*
Note: we cast away constness here, because using a codec can
modify it, and thus the Format_descr<T> must be mutable.
*/
return const_cast<Format_descr<T>&>(
m_fmt.get<Format_descr<T>>()
);
}
};
}} // impl::common
MYSQLX_ABI_BEGIN(2,0)
namespace common {
using impl::common::Format_info;
using impl::common::Format_descr;
/*
Storage for single column meta-data.
This extends Fromat_info with members used to store other column meta-data
such as its name etc.
Note: All textual data is stored as utf8 encoded strings.
Note: Column_info must be defined inside ABI namespace to preserve ABI
compatibility (name of this class is used in public API)
*/
class Column_info
: public Format_info
{
public:
using string = std::string;
string m_name;
string m_label;
string m_table_name;
string m_table_label;
string m_schema_name;
string m_catalog;
unsigned long m_length;
unsigned short m_decimals;
cdk::collation_id_t m_collation;
bool m_padded = false;
Column_info(const Column_info&) = default;
/*
Create Column_info instance for a column of type T that uses given
encoding format. The rest of column meta-data should be filled in using
store_info() method.
*/
template <cdk::Type_info T>
explicit Column_info(const Format_descr<T> &fmt)
: Format_info(fmt)
{}
/*
Create Clumn_info instance for a column which stores values of the given
type as raw bytes. The fi parameter describes the raw bytes storage
characteristics such as padding.
*/
explicit Column_info(cdk::Type_info type, const cdk::Format_info &fi)
: Format_info(type, Format_descr<cdk::TYPE_BYTES>(fi))
{}
/*
After creating Column_info instance this method should be called to
store information taken from cdk::Column_info interface.
*/
void store_info(const cdk::Column_info &ci)
{
m_name = ci.orig_name();
m_label = ci.name();
if (ci.table())
{
m_table_name = ci.table()->orig_name();
m_table_label = ci.table()->name();
if (ci.table()->schema())
{
m_schema_name = ci.table()->schema()->name();
if (ci.table()->schema()->catalog())
m_catalog = ci.table()->schema()->catalog()->name();
}
}
m_collation = ci.collation();
m_length = ci.length();
ASSERT_NUM_LIMITS(short unsigned, ci.decimals());
m_decimals = static_cast<short unsigned>(ci.decimals());
if (cdk::TYPE_BYTES == m_type)
{
uint64_t pad_width = get<cdk::TYPE_BYTES>().m_format.pad_width();
if (0 < pad_width)
{
m_padded = true;
assert(m_length == pad_width);
}
}
}
};
} // common
MYSQLX_ABI_END(2,0)
namespace impl {
namespace common {
using MYSQLX_ABI(2,0)::common::Column_info;
/*
Meta_data<STR> holds type and format information for all columns in
a result. This information is stored in Column_info<STR> objects. The
string type STR is used to internally store textual meta-data information.
*/
struct Meta_data
{
protected:
cdk::col_count_t m_col_count = 0;
std::map<cdk::col_count_t, Column_info> m_cols;
public:
/*
Create Meta_data instance and fill it using meta-data information
read from the cdk::Meta_data interface.
*/
Meta_data(cdk::Meta_data&);
virtual ~Meta_data() {}
col_count_t col_count() const { return m_col_count; }
/*
Return meta-data information for the column at the given position.
*/
const Column_info& get_column(cdk::col_count_t pos) const
{
return m_cols.at(pos);
}
const Format_info& get_format(cdk::col_count_t pos) const
{
return m_cols.at(pos);
}
cdk::Type_info get_type(cdk::col_count_t pos) const
{
return get_format(pos).m_type;
}
private:
/*
Add to this Meta_data instance information about column
at position `pos`. The type and format information is given
by cdk::Format_info object, additional column meta-data by
cdk::Column_info object.
*/
template<cdk::Type_info T>
void add(
cdk::col_count_t pos,
const cdk::Column_info &ci,
const cdk::Format_info &fi
)
{
m_cols.emplace(pos, Column_info(Format_descr<T>(fi)));
m_cols.at(pos).store_info(ci);
}
/*
Add raw column information (whose values are presented as
raw bytes).
*/
void add_raw(
cdk::col_count_t pos,
const cdk::Column_info &ci,
cdk::Type_info type,
const cdk::Format_info &fi
)
{
m_cols.emplace(pos, Column_info(type, fi));
m_cols.at(pos).store_info(ci);
}
};
using Shared_meta_data = std::shared_ptr<Meta_data>;
/*
Create Meta_data instance using information provided by
cdk::Meta_data interface.
This costructor calls appropriate add() methods to add Column_info
instances to the m_cols map.
*/
inline
Meta_data::Meta_data(cdk::Meta_data &md)
{
m_col_count = md.col_count();
for (col_count_t pos = 0; pos < m_col_count; ++pos)
{
cdk::Type_info ti = md.type(pos);
const cdk::Format_info &fi = md.format(pos);
const cdk::Column_info &ci = md.col_info(pos);
switch (ti)
{
case cdk::TYPE_STRING: add<cdk::TYPE_STRING>(pos, ci, fi); break;
case cdk::TYPE_INTEGER: add<cdk::TYPE_INTEGER>(pos, ci, fi); break;
case cdk::TYPE_FLOAT: add<cdk::TYPE_FLOAT>(pos, ci, fi); break;
case cdk::TYPE_DOCUMENT: add<cdk::TYPE_DOCUMENT>(pos, ci, fi); break;
case cdk::TYPE_DATETIME: add<cdk::TYPE_DATETIME>(pos, ci, fi); break;
case cdk::TYPE_GEOMETRY: add<cdk::TYPE_GEOMETRY>(pos, ci, fi); break;
case cdk::TYPE_XML: add<cdk::TYPE_XML>(pos, ci, fi); break;
default:
add_raw(pos, ci, ti, fi);
break;
}
}
}
/*
Handling result data
====================
*/
/*
Convenience wrapper around std container that is used
to store incoming raw bytes sequence.
TODO: More efficient implementation.
*/
class Buffer
{
std::vector<byte> m_impl;
public:
void append(cdk::bytes data)
{
m_impl.insert(m_impl.end(), data.begin(), data.end());
}
size_t size() const { return m_impl.size(); }
cdk::bytes data() const
{
return cdk::bytes((byte*)m_impl.data(), m_impl.size());
}
};
/*
Data structure used to hold raw row data. It holds a Buffer with
raw bytes for each non-null field of a row.
*/
typedef std::map<col_count_t, Buffer> Row_data;
/*
Implementation for a single Row instance. It holds a copy of
raw data and a shared pointer to row set meta-data.
It is possible to create an empty Row_impl instance and populate it using
set() method. Such Row_impl instance does not correspond to a row received
from the server, but represents a row that is defined by the user (for
example, to be later inserted into a table).
This template is parametrized by VAL class, such as commmon::Value used
to convert and store result data. Converted values are stored as instances
of VAL class in m_vals map and method get() returns references to these
instances.
Note: VAL class must define static method template used for converting raw
bytes into values:
template <cdk::Type_info T>
Value Value::Access::mk(cdk::bytes data, mysqlx::Format_descr<T>&)
*/
template <class VAL = Value>
class Row_impl
{
public:
using Value = VAL;
using bytes = cdk::bytes;
Row_impl() {};
// Note: row data is copied into Row_impl instance
Row_impl(const Row_data &data, const Shared_meta_data &md)
: m_data(data), m_mdata(md)
{}
protected:
Row_data m_data;
Shared_meta_data m_mdata;
std::map<col_count_t, Value> m_vals;
col_count_t m_col_count = 0;
public:
void clear()
{
m_data.clear();
m_vals.clear();
m_mdata.reset();
}
col_count_t col_count() const
{
return m_mdata ? m_mdata->col_count() : m_col_count;
}
bytes get_bytes(col_count_t pos) const
{
if (m_mdata && pos >= m_mdata->col_count())
throw std::out_of_range("row column");
auto data = m_data.find(pos);
// Note: no data at given pos means null value.
return data != m_data.end() ? data->second.data() : bytes{};
}
/*
Get value of field at given position after converting to Value.
@throws std::out_of_range if given column does not exist in the row.
*/
Value& get(col_count_t pos)
{
if (m_mdata && pos >= m_mdata->col_count())
throw std::out_of_range("row column");
auto val = m_vals.find(pos);
if (val != m_vals.end())
return val->second;
if (!m_mdata)
throw std::out_of_range("no meta-data found");
const Format_info &fi = m_mdata->get_format(pos);
convert_at(pos, fi);
return m_vals.at(pos);
}
void set(col_count_t pos, const Value &val)
{
m_vals.emplace(pos, val);
if (pos >= m_col_count)
m_col_count = pos + 1;
}
private:
void convert_at(col_count_t pos, const Format_info &fi)
{
Buffer *raw = nullptr;
auto data = m_data.find(pos);
if (data != m_data.end())
raw = &(data->second);
if (!raw || 0 == raw->size())
{
// Null value
m_vals.emplace(pos, Value());
return;
}
/*
Call static function VAL::Access:mk() to construct VAL instance from
raw bytes and put it into m_vals map. Aprropriate encoding format
information is extracted from fi.
*/
#define CONVERT(T) case cdk::TYPE_##T: \
m_vals.emplace(pos, \
VAL::Access::mk(raw->data(), fi.get<cdk::TYPE_##T>()) \
); \
break;
switch (fi.m_type)
{
CDK_TYPE_LIST(CONVERT)
}
}
};
/*
Given encoding format information, convert raw bytes to the corresponding
value.
*/
Value convert(cdk::bytes, Format_descr<cdk::TYPE_STRING>&);
Value convert(cdk::bytes, Format_descr<cdk::TYPE_INTEGER>&);
Value convert(cdk::bytes, Format_descr<cdk::TYPE_FLOAT>&);
Value convert(cdk::bytes, Format_descr<cdk::TYPE_DOCUMENT>&);
Value convert(cdk::bytes, Format_descr<cdk::TYPE_DATETIME>&);
/*
Generic template used when no type-specific specialization is defined.
It builds a value holding the raw bytes.
*/
template <cdk::Type_info T>
Value convert(cdk::bytes data, Format_descr<T>&)
{
/*
Note: Trailing '\0' byte is used for NULL value detection and is not
part of the data
*/
return{ data.begin(), data.size() - 1 };
}
}} // impl::common
MYSQLX_ABI_BEGIN(2, 0)
namespace common {
/*
This static function is called by Row_impl to build column value from raw
bytes.
*/
template<cdk::Type_info T>
inline
Value Value::Access::mk(cdk::bytes data, impl::common::Format_descr<T> &format)
{
// Use convert() to convert raw bytes into a value
Value val{ convert(data, format) };
/*
Store raw representation in m_str if possible (for RAW value this is
already done by the constructor).
*/
switch (val.get_type())
{
case Value::RAW:
case Value::STRING:
case Value::VNULL:
break;
default:
/*
Note: Trailing '\0' byte is used for NULL value detection and is not
part of the data
*/
val.m_str.assign(data.begin(), data.end() - 1);
break;
}
return val;
}
} // common
MYSQLX_ABI_END(2, 0)
/*
Implementation of result object
===============================
Result object of class Result_impl<STR> gives access to a reply sent
by the server in response to a query. It gives information about the reply
such as whether it contains row data or affected items counts etc. If reply
contains row data, result object stores and gives access to meta-data about
this row data. It also handles reading rows from the reply. A result object
can handle server replies which contain multiple results.
Template parameter STR is a string class used for storing textual meta-data
- the get_column() method presents column meta-data as Column_info<STR>
instances.
A result object is created from a Result_init instance which contains
information about the session and CDK reply object used to access server
reply. Method Executable_if::execute() implemented by objects representing
queries returns such a Result_init object that is then used to initialize
a result instance.
*/
MYSQLX_ABI_BEGIN(2,0)
namespace common {
using impl::common::Shared_meta_data;
using impl::common::Row_data;
using impl::common::Column_info;
/*
An abstract interface used to initialize result of an operation.
An object implementing this interface can be used to construct a result
object (see ctor of Result_impl_base).
Note: This class must be defined inside ABI namespace to preserve ABI
compatibility (its name is used in public API)
*/
class Result_init
{
public:
virtual Shared_session_impl get_session() = 0;
/*
Return pointer to the cdk reply object which gives access to results
returned by the server. The caller of this method takes ownership of
the reply object.
*/
virtual cdk::Reply* get_reply() = 0;
/*
A hook that can perform additional initialization of the result object
being constructed from a Result_init instance.
*/
virtual void init_result(Result_impl&) {} // GCOV_EXCL_LINE
};
/*
Base class for Result_impl<STR> with all members that are not dependent
on the STR template parameter.
Given a server reply to a command, it processes the reply giving access to
the result data and meta-data.
Note: This class must be defined inside ABI namespace to preserve ABI
compatibility (its name is used in public API)
*/
class Result_impl
: public cdk::Row_processor
, public cdk::api::Diagnostics
{
public:
Result_impl(Result_init &init);
virtual ~Result_impl();
/*
Prepare for reading (the next) result.
This method should be called first, before any other methods which access
the result. After consuming one result, it should be called again to see
if more results are pending (which can happen in case of a multi-result
reply from the server).
Returns true if (the next) result is ready for reading. Otherwise there
are no more results and server reply has been entirely consumed. Note that
the result does not need to contain row data - this can be determined by
has_data() method.
*/
bool next_result();
/*
Returns true if the current result has (more) rows to be fetched.
*/
bool has_data() const;
/*
Return reference to result meta-data. Note that returned meta-data can
be used together with Row_data from get_row() to build a Row_impl
instance.
*/
const Shared_meta_data& get_mdata() const;
/*
Fetches next row from the result, if any. Returns NULL if there are no
more rows. Throws exception if this result has no data.
Note: Rows are cached internally and read in larger batches.
*/
const Row_data *get_row();
// Store all remaining rows in the internal cache.
void store();
// Store all results to cache
void store_all_results();
/*
Return the number of rows remaining in the result (the rows that have been
already fetched with get_row() are not counted).
This has a side effect of storing all rows in the chache.
*/
row_count_t count();
/*
Discard the reply. TODO: Implement it when needed.
*/
//void discard();
/*
Methods to access result information
*/
col_count_t get_col_count() const;
cdk::row_count_t get_affected_rows() const;
cdk::row_count_t get_auto_increment() const;
unsigned get_warning_count() const;
/*
Client-side filtering of row data. Function m_row_filter is applied
for each received row to determine if it should be skipped.
*/
using Row_filter_t = std::function<bool(const Row_data&)>;
Row_filter_t m_row_filter = [](const Row_data&) { return true; };
// Get generated document id information.
const std::vector<std::string>& get_generated_ids() const;
/*
Get column information.
Returns reference to Column_info<STR> object with information about
the column. This Column_info<STR> object sits inside and is owned by
this Result_impl object.
*/
const Column_info& get_column(col_count_t pos) const
{
if (m_result_mdata.empty() || !m_result_mdata.front())
THROW("No result set");
return m_result_mdata.front()->get_column(pos);
}
protected:
Result_impl(const Result_impl&) = delete;
/*
Note: Session implementation must exists as long as this result
implementation exists. Thus we keep shared pointer to it to make sure
that session implementation is not destroyed when session object itself
is deleted.
*/
Shared_session_impl m_sess;
// -- Result meta-data
/*
This flag is set to true after next_result(), when the result has been
prepared.
*/
bool m_inited = false;
// Note: meta-data can be shared with Row instances
std::queue<Shared_meta_data> m_result_mdata;
/*
Method fetch_meta_data() creates a new Meta_data_base instance with
the information taken from a CDK cursor object (or any other object
implementing cdk::Meta_data interface). This is called from next_result()
when meta-data is available. The returned object is stored in m_mdata which
takes its ownership.
*/
//Meta_data_base* fetch_meta_data(cdk::Meta_data&) = 0;
// -- Result data
/*
This flag is true if there are pending rows that server sends and that
should be consumed.
*/
bool m_pending_rows = false;
/*
Note: Not using auto-ptrs for m_reply and m_cursor to ensure correct
order of deleting them (see dtor).
*/
cdk::Reply *m_reply;
cdk::Cursor *m_cursor = nullptr;
using Row_cache = std::forward_list<Row_data>;
// Each queue elements represents a resultset.
std::queue<Row_cache> m_result_cache;
std::queue<row_count_t> m_result_cache_size;
Row_cache::iterator m_cache_it;
/*
Ensure some rows are loaded into the cache. If cache is not empty, it
returns true right away. Otherwise it loads rows into the cache. If
preftetch_size is non-zero then at most that many rows are loaded.
Returns true if some rows are present in the cache.
This will cache the current resultset to the back of the queue
*/
bool load_cache(row_count_t prefetch_size = 0);
// Jumps to new resultset without poping the cache element
bool read_next_result();
// Called after resultset has been get by user
void pop_row_cache()
{
auto lock = m_sess->lock();
if(!m_result_mdata.empty())
m_result_mdata.pop();
if (!m_result_cache.empty())
{
/*
At this point m_cache_it can point inside the last entry of
m_result_cache that is just to be removed. Such dangling iterator
can cause issues when later it is assigned to a new value (as
compiler thinks it is still pointing inside old container and might
want to do some cleanups). To avoid dangling iterator, we reset it
to something neutral here.
Note: This "fix" works under assumption that "one past the end"
iterator is compatible between different containers, which seems to
be the case for all compilers we use.
TODO: A better solution would be to use std::option<> type for
m_cache_it.
*/
m_cache_it = m_result_cache.back().end();
m_result_cache.pop();
}
if (!m_result_cache_size.empty())
m_result_cache_size.pop();
}
// Called on each resultset to be read.
void push_row_cache();
public:
// -- Diagnostic information
// Return number of diagnostic entries with given error level (defaults to ERROR).
unsigned int entry_count(Severity::value level = Severity::ERROR) override
{
auto lock = m_sess->lock();
if (!m_reply)
THROW("Attempt to get warning count for empty result");
return m_reply->entry_count(level);
}
// Get an iterator to iterate over diagnostic entries with level above or equal to given one
// (for example, if level is WARNING then iterates over all warnings and errors).
// By default returns iterator over errors only. The Error_iterator interface extends
// Iterator interface with single Error_iterator::error() method that returns the current error entry from the sequence.
Iterator& get_entries(Severity::value level = Severity::ERROR) override
{
auto lock = m_sess->lock();
if (!m_reply)
THROW("Attempt to get warning count for empty result");
return m_reply->get_entries(level);
}
// Convenience method to return first error entry (if any).
// Equivalent to get_erros().error(). Note that this method can throw exception if there is no error available.
const cdk::Error& get_error() override
{
auto lock = m_sess->lock();
if (!m_reply)
THROW("Attempt to get warning count for empty result");
return m_reply->get_error();
}
private:
// Row_processor
Row_data m_row;
bool row_begin(row_count_t) override
{
m_row.clear();
return true;
}
void row_end(row_count_t) override;
size_t field_begin(col_count_t pos, size_t) override;
void field_end(col_count_t) override {}
void field_null(col_count_t) override {}
size_t field_data(col_count_t pos, bytes) override;
void end_of_data() override;
};
inline
col_count_t Result_impl::get_col_count() const
{
auto lock = m_sess->lock();
if (m_result_mdata.empty())
THROW("No result set");
return m_result_mdata.front()->col_count();
}
inline
cdk::row_count_t Result_impl::get_affected_rows() const
{
auto lock = m_sess->lock();
if (!m_reply)
THROW("Attempt to get affected rows count on empty result");
return m_reply->affected_rows();
}
inline
cdk::row_count_t Result_impl::get_auto_increment() const
{
auto lock = m_sess->lock();
if (!m_reply)
THROW("Attempt to get auto increment value on empty result");
return m_reply->last_insert_id();
}
inline
unsigned Result_impl::get_warning_count() const
{
auto lock = m_sess->lock();
auto self = const_cast<Result_impl*>(this);
self->store_all_results();
return self->entry_count(cdk::api::Severity::WARNING);
}
inline
const std::vector<std::string>& Result_impl::get_generated_ids() const
{
auto lock = m_sess->lock();
if (!m_reply)
THROW("Attempt to get generated ids for empty result");
return m_reply->generated_ids();
}
inline
bool Result_impl::has_data() const
{
auto lock = m_sess->lock();
return (!m_result_cache.empty() && !m_result_cache.front().empty()) || m_pending_rows;
}
inline
const Shared_meta_data& Result_impl::get_mdata() const
{
auto lock = m_sess->lock();
return m_result_mdata.front();
}
inline
void Result_impl::store()
{
auto lock = m_sess->lock();
load_cache();
}
inline
void Result_impl::store_all_results()
{
auto lock = m_sess->lock();
do{
store();
}while(read_next_result());
}
inline
row_count_t Result_impl::count()
{
auto lock = m_sess->lock();
store();
if (entry_count() > 0)
get_error().rethrow();
row_count_t rc = 0;
if(!m_result_cache_size.empty())
rc = m_result_cache_size.front();
return rc;
}
} // common
MYSQLX_ABI_END(2,0)
} // mysqlx
#endif