WL#9509: X Protocol connector code extraction from mysqlxtest to libmysqlxclient

Affects: Server-8.0   —   Status: Complete

Summary
=======

This worklog implements a low level client/connector library that is going to
be used by "mysqlxtest", "ngshell", "XCom", internal component, external component to connect to MySQL Server using X Protocol.


Details
=======

C++ connector that uses X Protocol is already in MySQL Server tree, still
it is part of "mysqlxtest" executable and it isn't accessible for other
components. The connector functionality needs to be extracted from "mysqlxtest"
into a static library, which can be linked/used by any other target.


The library is going to be used by:

* "mysqlxtest" executable
* "mysqltest" executable 


All MySQL Server executables should be able to link with it. The same goes for
MySQL Servers plugins.

Creating the library as static has one important advantage in comparison to
shared libraries. The library can't be used with wrong version of interface/
application. Both are shipped in single application.


High-Level API Functional Requirements
======================================

* User must be able to connect to X Plugin
* User must be able to start TLS if X Plugin supports it
* User must be able to authenticate against MySQL Server account created with
  `sha256_password` or `mysql_native` plugin
* User must be able to force usage of expired MySQL Server account created with
  `sha256_password` or `mysql_native` plugin
* User must be able to execute SQL command and receive its result-set or
  multi-result-set
* User must be able to set/get supported client options
* User should receive an error when he tries to execute unsupported operation
* User should receive an error when he tries to set/get unsupported option
* User must be able to choose `Mysqlx` library that depends on protobuf-lite or
  protobuf
* User must be able to execute admin command and receive its result-set
* User must be able to execute CRUD command and receive its result-set
* User must be able to receive X Protocol notices (local) when executing
  CRUD or SQL command
* User must be able to receive X Protocol notices (global) when executing
  CRUD or SQL command


Low-Level API Functional Requirements
=====================================

* When new message type is added to proto files, user must be able to send the
  new defined message without interface extension
* When new message type is added to proto files, user must be able to receive
  the new defined message without interface extension
* User must be able to send and receive messages directly, executing any flow
  that is supported by X Plugin and is not supported by the library


Interface
=========

X Protocol configuration script generates protocol message using lite
and full version of 'protobuf' at once. The lite or full version can be used
with X Plugin. Full version gives X Plugin possibility to trace sent/received
messages in text form in error log. The "mysqlxtest" application always
uses full version. The connector must work with both version of protobuf
messages: full and lite.

The X connector is going to consist of two components: 'mysqlxclient' and
'mysqlxmessages', both components are static libraries. The first one is going
to implement X Protocol flows, network transmission, session management.
Those functionalities were grouped and put in following interfaces: XSession,
XProtocol, XConnection.

``` plantuml
interface XMessages
interface Protobuf

               [mysqlxclient Component] <<static library>>
XConnection -- [mysqlxclient Component]
XSession    -  [mysqlxclient Component]
XProtocol   -- [mysqlxclient Component]
               [mysqlxclient Component] ..> XMessages
               [mysqlxclient Component] ..> Protobuf
```

Second component is going to hold X Protocol messages generated from 'proto'
files. The differentiation of those components is needed because
'mysqlxmessage' is going to be reused by 'X Plugin', 'mysqlxclient',
"mysqlxtest".


``` plantuml
interface XMessages
interface Protobuf


   [mysqlxmessage Component] <<static library>>
   [mysqlxmessage Component] -- XMessages
   [mysqlxmessage Component] ..> Protobuf
```

In the end, user is going to get four components, two mentioned above need
to have full and lite version with their version of interfaces:


``` plantuml
 interface XMessages
 interface XMessages_lite
 interface Protobuf

 [mysqlxmessage Component] <<static library>>
 [mysqlxmessage Component] -- XMessages
 [mysqlxmessage Component] ..> Protobuf

 [mysqlxmessage_lite Component] <<static library>>
 [mysqlxmessage_lite Component] -- XMessages_lite
 [mysqlxmessage_lite Component] ..> Protobuf
```

``` plantuml
interface XMessages
interface XMessages_lite
interface Protobuf

               [mysqlxclient Component] <<static library>>
XConnection -- [mysqlxclient Component]
XSession    -  [mysqlxclient Component]
XProtocol   -- [mysqlxclient Component]
               [mysqlxclient Component] ..> XMessages
               [mysqlxclient Component] ..> Protobuf


                    [mysqlxclient_lite Component] <<static library>>
XConnection_lite -- [mysqlxclient_lite Component]
XProtocol_lite   -- [mysqlxclient_lite Component]
                    [mysqlxclient_lite Component] - XSession_lite
                    [mysqlxclient_lite Component] ..> XMessages_lite
                    [mysqlxclient_lite Component] ..> Protobuf
```

The client application is going to use 'mysqlxclient' library to create
a communication channel between the client and the MySQL Server (using
X Protocol) the interaction between components looks like:

``` plantuml
package Client {
   interface XMessages
                 [mysqlxclient Component] <<static library>>
  XConnection -- [mysqlxclient Component]
  XProtocol   -- [mysqlxclient Component]
  XSession    -- [mysqlxclient Component]
  XMessages  <.. [mysqlxclient Component]

               [client Component]   <<executable>>
               [client Component] ..> () XSession : use
               [client Component] ..> () XProtocol : use
               [client Component] ..>  () XConnection : use
  XMessages <. [client Component]


  [mysqlxmessages Component] <<static library>>
  [mysqlxmessages Component] - XMessages
}

package Server {
  [mysqlxclient Component]-[X Plugin] : TCP/X Protocol
}
```

XSession interface
------------------

The interface is the first acquired by the client application, is responsible
for configuration things like SSL, support expired password, force
authentication method, I/O timeouts, host resolver. In general all the lower
layer settings (XProtocol, XConnection). It also opens a channel to MySQL
Server and authenticates. When the session is ready it allows to execute SQL
statements and "admin commands", things that doesn't need to take as argument
a X Protocol messages. All operations are synchronous.

XProtocol interface
-------------------

XProtocol is responsible for deserialization/serialization of X Protocol
messages, also it defines some basic flows or parts of flows. User is going
to find in it all X Protocol specific functionalities, like: CRUD execution,
expectation blocks, handling of notices, pipelining.
In case when the interface misses a flow, user can implement it by sending/
receiving messages in a sequence defined by the flow. Even when the messages
don't have their send/receive method then a generic function can by used.

XConnection interface
---------------------

XConnection is responsible for setting up communication channel (currently TCP,
UNIX socket). It means that its also responsible for SSL functionality or
future functionalities like compression. It gives user the possibility
to ignore the protobuf wire format and X Protocol format/flow, doing raw
read/write operations. With it user could implement its custom data streamer
or do some optimization like skipping protobuf message serialization process.


Configuration
=============

CMake build options
-------------------

X Plugin has a cmake variable which controls used version of 'protobuf'.
The current definition looks as follows:

|Property      |Value                    |
|--------------|-------------------------|
|Name          |XPLUGIN_NO_LITE_PROTOBUF |
|Default value |OFF                      |
|Allowed values|0, 1, "ON", "OFF"        |

The variable should be changed to an option variable:

|Property      |Value                                   |
|--------------|----------------------------------------|
|Name          |DISABLE_MYSQLX_PROTOBUF_LITE            |
|Default value |OFF                                     |
|Allowed values|0, 1, "ON", "OFF"                       |
|Description   |Choose version of protobuf library which|
|              |should be linked against X Plugin       |


Error and Warnings
==================

Client side error codes use the same values as libmysqlclient whenever
possible. For error codes that are specific for  'mysqlxclient', it is
going to use values above 2500 to avoid clashes.

Reused MySQL client error names:
* CR_ALREADY_CONNECTED
* CR_COMMANDS_OUT_OF_SYNC
* CR_CONNECTION_ERROR
* CR_INVALID_CONN_HANDLE
* CR_MALFORMED_PACKET
* CR_SERVER_GONE_ERROR
* CR_SOCKET_CREATE_ERROR
* CR_SSL_CONNECTION_ERROR
* CR_UNKNOWN_ERROR
* CR_UNKNOWN_HOST

To mark the errors that are specific for 'mysqlxclient' they were prepended
with "CR_X_":

|Property    |Value                                         |
|------------|----------------------------------------------|
|Name        |CR_X_READ_TIMEOUT                             |
|Error code  |2500                                          |
|Error text  |Read operation failed because of a timeout    |

|Property    |Value                                         |
|------------|----------------------------------------------|
|Name        |CR_X_WRITE_TIMEOUT                            |
|Error code  |2501                                          |
|Error text  |Write operation failed because of a timeout   |

|Property    |Value                                         |
|------------|----------------------------------------------|
|Name        |CR_X_INTERNAL_ABORTED                         |
|Error code  |2502                                          |
|Error text  |Aborted by internal callback at %s processing |

|Property    |Value                                         |
|------------|----------------------------------------------|
|Name        |CR_X_TLS_WRONG_CONFIGURATION                  |
|Error code  |2503                                          |
|Error text  |TLS was marked %s, but it was not configured  |

|Property    |Value                                         |
|------------|----------------------------------------------|
|Name        |CR_X_INVALID_AUTH_METHOD                      |
|Error code  |2504                                          |
|Error text  |Invalid authentication method %s              |

|Property    |Value                                         |
|------------|----------------------------------------------|
|Name        |CR_X_UNSUPPORTED_OPTION_VALUE                 |
|Error code  |2505                                          |
|Error text  |Invalid value for %s                          |

|Property    |Value                                         |
|------------|----------------------------------------------|
|Name        |CR_X_UNSUPPORTED_CAPABILITY_VALUE             |
|Error code  |2506                                          |
|Error text  |Capability not supported                      |

|Property    |Value                                         |
|------------|----------------------------------------------|
|Name        |CR_X_UNSUPPORTED_OPTION                       |
|Error code  |2507                                          |
|Error text  |Option not supported                          |

|Property    |Value                                         |
|------------|----------------------------------------------|
|Name        |CR_X_LAST_COMMAND_UNFINISHED                  |
|Error code  |2508                                          |
|Error text  |Fetching wrong result set, there is previous  |
|            |command pending.                              |

|Property    |Value                                         |
|------------|----------------------------------------------|
|Name        |CR_X_RECEIVE_BUFFER_TO_SMALL                  |
|Error code  |2509                                          |
|Error text  |Receive buffer to small                       |

Interface usage example
=======================

SQL execution
-------------

#include <cassert>
#include <iostream>
#include <mysqlxclient.h>

int main(int argc, const char *argv[]) {
    xcl::XError error;
    auto session = xcl::create_session(
        "127.0.0.1",
        33060,
        "root",
        "",
        "schema",
        &error);

    if (nullptr == session.get()) {
        std::cout << error.error() << ", info: " << error.what() << "\n";
        return -1;
    }

    auto resultset = session->execute_sql(
        "SHOW STATUS LIKE 'Mysqlx_address'",
        &error);

    auto columns = resultset->get_metadata();

    const xcl::XRow *row;
    int number_or_rows = 0;
    for(const auto &column : columns) {
        std::cout << column.name << " ";
    }

    std::cout << "\n";

    while (row = resultset->get_next_row()) {
        ++number_or_rows;
        for(int index = 0; index < columns.size(); ++index) {
            std::string field_as_string;

            assert(!row->is_null(index));
            row->get_field_as_string(index, &field_as_string);

            std::cout  << field_as_string << " ";
        }
        std::cout << "\n";
    }

    assert(2 == columns.size());
    assert(1 == number_or_rows);

    return 0;
}

Example of streaming data through notices
-----------------------------------------

#include <cassert>
#include <iostream>
#include <mysqlxclient.h>

const uint32_t PLUGIN_TYPE_BINLOG_STREAM = 50;
const uint32_t TEST_BINLOG_FILESIZE = 1000;

int main(int argc, const char *argv[]) {
    xcl::XError error;
    uint32_t written = 0;
    auto session = xcl::create_session(
        "127.0.0.1",
        33060,
        "root",
        "",
        "schema",
        &error);

    if (nullptr == session.get()) {
        std::cout << error.error() << ", info: " << error.what() << "\n";
        return -1;
    }

    // Notices are asynchronous. If server was triggered to send
    // them then while executing sql or stmt
    session->get_protocol().push_notice_handler(
        [&session, &written](
                xcl::XProtocol *protocol,
                const bool is_global,
                const Mysqlx::Notice::Frame::Type type,
                const char * payload,
                const uint32_t payload_size) -> xcl::Handler_result {
            // Handle async notices
            const auto plugin_type = static_cast<uint32_t>(type);

            if (is_global || plugin_type != PLUGIN_TYPE_BINLOG_STREAM) {
                return xcl::Handler_continue;
            }

            written += payload_size;

            if (written >= TEST_BINLOG_FILESIZE) {
                // Operation was successful
                // still we need to break the program
                // with internal error
                return xcl::Handler_error;
            }

            return xcl::Handler_consumed;
        });

    using Argument  = xcl::Argument_value;
    using Object    = xcl::Argument_value::Object;
    Argument argument{ Object{
        {"start_pos", Argument(1000UL)},
        {"flags",     Argument(16UL)},
        {"server_id", Argument(2332UL)},
        {"file_name", Argument("binlog.file")}}
    };

    // STEP 1
    // Tell binlog_plugin component to
    // start streaming binlog files
    // through X Plugin using async notices
    //
    // The functionality that allows to register
    // custom stmt namespaces (in this case "binlog_plugin")
    // is going to be delivered under:
    // WL#9847: Component service X Plugin transport layer
    auto resultset = session->execute_stmt(
        "binlog_plugin",
        "start",
        {argument},
        &error);

    // Verify if the command was processed by server
    // without any errors
    while(resultset->next_resultset(&error)) {};

    if (error) {
        std::cout
            << "Streaming was not started because on error: "
            << error.error() << ", info: " << error.what() << "\n";
        return -1;
    }

    xcl::XProtocol::Message_id out_message_id;

    // STEP 2
    // Lets try receive any message.
    // We expect only notices, other message should cause an error.
    // Notices could be received while "execute_stmt" in STEP 1,
    // Thus reading them through "recv_single_message wouldn't be
    // sufficinet and could cause "notice" loses. Handling notices
    // in a callback guarantees that at any moment we are going to
    // receive them asynchronous.
    while (!error)
        session->get_protocol().recv_single_message(
            &out_message_id,
            &error);

    if (error && error.error() != CR_X_INTERNAL_ABORTED) {
        std::cout
            << "Streaming failed with error: "
            << error.error() << ", info: " << error.what() << "\n";
        return -1;
    }

    return 0;
}

Example of custom message flow
------------------------------

#include <cassert>
#include <iostream>
#include <memory>
#include <mysqlxclient.h>

const unsigned EXPECT_NO_ERROR = 1;

void operator<< (::google::protobuf::Message &msg, const std::string& txt)
{
  assert(::google::protobuf::TextFormat::ParseFromString(txt, &msg));
}

bool check_for_error(const xcl::XError &error) {
    if (error) {
        std::cout << "Error code: "
                << error.error()
                << ", info: "
                << error.what()
                << std::endl;
        return true;
    }

    return false;
}

class Printer {
public:
    Printer(std::unique_ptr<xcl::XQuery_result> &&resultset)
    : m_resultset(std::move(resultset)) {
    }

    bool print() {
        xcl::XError error;

        do {
            print_metadata();
            print_rows();
        } while(m_resultset->next_resultset(&error));

        return check_for_error(error);
    }

private:
    void print_metadata() {
        for(const auto &column : m_resultset->get_metadata()) {
            std::cout << column.name << " ";
        }

        std::cout << std::endl;
    }

    void print_rows() {
        const xcl::XRow *row;

        while (row = m_resultset->get_next_row()) {
            for(int i = 0; i < row->get_number_of_fields(); ++i) {
                std::string fields_text;

                row->get_field_as_string(i, &fields_text);
                std::cout << fields_text << " ";
            }
            std::cout << std::endl;
        }
    }

    std::unique_ptr<xcl::XQuery_result> m_resultset;
};


int main(int argc, const char *argv[]) {
    xcl::XError error;
    uint32_t written = 0;
    auto session = xcl::create_session(
        "127.0.0.1",
        33060,
        "root",
        "",
        "schema",
        &error);

    if (nullptr == session.get()) {
        check_for_error(error);
        return -1;
    }

    // Protobuf messages
    ::Mysqlx::Expect::Open     msg_open;
    ::Mysqlx::Expect::Close    msg_close;
    ::Mysqlx::Sql::StmtExecute msg_stmt1;
    ::Mysqlx::Sql::StmtExecute msg_stmt2;
    ::Mysqlx::Sql::StmtExecute msg_stmt3;

    // condition_key:1 => EXPECT_NO_ERROR
    msg_open  << "cond { condition_key: 1 }";

    // Unknown column 'ASDDS1' in 'field list'
    msg_stmt1 <<  "stmt : \"select ASDDS1 as 'Col1'\"";
    msg_stmt2 <<  "stmt : \"select 2 as 'Col1'\"";
    msg_stmt3 <<  "stmt : \"select 3 as 'Col1'\"";

    auto &protocol = session->get_protocol();

    protocol.send(msg_open);
    if (check_for_error(protocol.recv_ok()))
        return -1;

    // Start PIPELINING
    protocol.send(msg_stmt1);
    protocol.send(msg_stmt2);
    protocol.send(msg_stmt3);

    // Receive the response from the PIPELINE

    // Wrong query generates and error
    Printer(protocol.recv_resultset()).print();
    // Expect open and previous error cause
    // to skip this query. Server generates error.
    Printer(protocol.recv_resultset()).print();
    // Expect open and previous error cause
    // to skip this query. Server generates error.
    Printer(protocol.recv_resultset()).print();

    protocol.send(msg_close);
    if (check_for_error(protocol.recv_ok()))
        return -1;

    return 0;
}


Example of custom protocol running on X
---------------------------------------

#include <cassert>
#include <iostream>
#include <memory>
#include <mysqlxclient.h>

// Header with frame builder for new protocol
// class RequestTask
// class ResponseTask
// method: std::string custom_encode(const RequestTask &);
// method: ResponseTask custom_decode_response(std::string);
// method: ResponseTask custom_decode_response(std::string);
// define: CUSTOM_PROTOCOL_MAX_FRAME 2000
#include "custom_protocol.h"

// Custom messages are not supported by X Plugin
// anyway this functionality is going to be added with
// WL#9847
const xcl::XProtocol::Message_type_id CUSTOM_PROTOCOL_FRAME = 255;

bool check_for_error(const xcl::XError &error) {
    if (error) {
        if (CR_X_RECEIVE_BUFFER_TO_SMALL == error.error()) {
            std::cout << "Invalid custom protocol frame. Wrong size." << std::endl;

            return true;
        }

        std::cout << "Error code: "
                << error.error()
                << ", info: "
                << error.what()
                << std::endl;
        return true;
    }

    return false;
}

int main(int argc, const char *argv[]) {
    xcl::XError error;
    uint32_t written = 0;
    auto session = xcl::create_session(
        "127.0.0.1",
        33060,
        "root",
        "",
        "schema",
        &error);

    if (nullptr == session.get()) {
        check_for_error(error);
        return -1;
    }

    auto protocol = session->get_protocol();
    std::string receive_buffer(CUSTOM_PROTOCOL_MAX_FRAME, '\0');
    const auto  send_buffer = custom_encode(ResponseTask());

    error = protocol.send(CUSTOM_PROTOCOL_FRAME,
                          static_cast<uint8_t>(send_buffer.c_str()),
                          send_buffer.length());

    if (check_for_error(error))
        return -1;

    xcl::XProtocol::Message_type_id received_message_id;
    auto buffer      = static_cast<uint8_t>(receive_buffer.c_str());
    auto buffer_size = receive_buffer.length());
    error = protocol->recv(
        &received_message_id,
        &buffer,
        &buffer_size);

    if (check_for_error(error))
        return -1;

    auto result = custom_decode_response(receive_buffer);
    //...
    return 0;
}
CMake targets
=============

Following cmake targets should be extracted from "mysqlxtest"

xmessages, xmessages_lite
-------------------------

These libraries are going to contain compiled X Protocol messages.
Until now each target was generated and compiled with X Protocol messages on
its own. The libraries are going to compile X Protocol twice (full, lite)
and other project should link to them.
The C++ code is generated from following files:

* rapid/plugin/x/protocol/mysqlx_connection.proto
* rapid/plugin/x/protocol/mysqlx_crud.proto
* rapid/plugin/x/protocol/mysqlx_datatypes.proto
* rapid/plugin/x/protocol/mysqlx_expect.proto
* rapid/plugin/x/protocol/mysqlx_expr.proto
* rapid/plugin/x/protocol/mysqlx_notice.proto
* rapid/plugin/x/protocol/mysqlx_resultset.proto
* rapid/plugin/x/protocol/mysqlx_session.proto
* rapid/plugin/x/protocol/mysqlx_sql.proto
* rapid/plugin/x/protocol/mysqlx.proto

The generation output is placed under build directory at:

* rapid/plugin/x/generated/protobuf
* rapid/plugin/x/generated/protobuf_lite

Those folders need to be moved to:

* rapid/plugin/x/protocol/protobuf
* rapid/plugin/x/protocol/protobuf_lite

mysqlxclient, mysqlxclinet_lite
-------------------------------

The connector code already exists under 'mysqlxtest' target:

* rapid/plugin/x/mysqlxtest_src/mysqlx_connection.cc
* rapid/plugin/x/mysqlxtest_src/mysqlx_connection.h
* rapid/plugin/x/mysqlxtest_src/mysqlx_protocol.cc
* rapid/plugin/x/mysqlxtest_src/mysqlx_protocol.h
* rapid/plugin/x/mysqlxtest_src/mysqlx_resultset.cc
* rapid/plugin/x/mysqlxtest_src/mysqlx_resultset.h
* rapid/plugin/x/mysqlxtest_src/mysqlx_session.cc
* rapid/plugin/x/mysqlxtest_src/mysqlx_session.h

Those files need to be refactored and derived from "C++" interfaces mentioned
in "LLD/C++ interfaces section" and moved to the new location/target:

* rapid/plugin/x/client/  (implementation)
* rapid/plugin/x/client/mysqlxclient (interfaces)

The code is same for both "full" and "lite" version. The differentiation
is made only by including header from "rapid/plugin/x/protocol/protobuf" or
"rapid/plugin/x/protocol/protobuf_lite" based on  "USE_MYSQLX_FULL_PROTO"
definition (it should be added to package-configuration files).

mysqlxtest targets
------------------

Cleanup of "simple-test-script" should be done by extracting classes like
"Command", "Connection_manager", "Json_to_any_handler", all "Block_processor"
from "mysqlxtest.cc" and they should be placed into new files.
Additionally all commands and block processors must be placed in directory
named "processor". General propose files should be place in one dedicated
folder leaving "mysqlxtest.cc" in main folder of "mysqlxtest_src".


Testing
=======

X Plugins test suite is going to cover the library with functional and
integration tests. There is no need for new test-suite or third party
framework. Additionally the library is going to be covered with unit tests,
using dependency injection and strictmocks.

The creation of objects must be handled by a factory in order to separate
client application from the library, enabling 'mock' injection for application
testing.


C++ Interfaces
==============

XSession
--------

```cpp
/**
  Interface that manages session.

  This interface is responsible for creation, configuration, release of
  connection and session management.
  It is also owner of other objects that are required to maintain a session.
  "SQL" or admin command can be execute through this interface.
  X Protocol specific features (or flows) may require manual sending of
  X Protocol messages (user needs to acquire XProtocol interface).
*/
class XSession {
 public:
  /**
    Capabilities supported by client library.

    Capabilities are settings that are transfered between client and server
    to change the behavior of both ends of X Protocol.
    Capabilities listed here are handled by implementation of
    XSession class. Setting capability may require to do some additional
    reconfiguration of communication channel. For example setting TLS
    capability reconfigures communication channel to use encryption.
    When setting Capabilities manually (using XProtocol interface) user
    needs to remember about the possible reconfiguration.
    Capabilities must be set before connecting through XSession interface.
  */
  enum Mysqlx_capability {
    /**
      User can handle expired passwords.

      Tell the server that the authentication attempt shouldn't be rejected
      when client uses expired MySQL Servers account. This gives the user
      a possibility to change the password like in "interactive" client. This
      capability shoudn't be used on scripts, plugins, internal connections.

      Capability type: BOOL. Value: enable/disable the support.
    */
    Capability_can_handle_expired_password,
  };

  /**
    Configuration options.

    Each value defines separate configurable behavior which can't be changed
    after connection establishment.
  */
  enum class Mysqlx_option {
    /** Defines behavior of `hostname` resolver:

     * "ANY": IPv4 and IPv6 addresses are accepted when `hostname`
       is resolved.
     * "IP4": IPv4 addresses are accepted when `hostname`
       is resolved.
     * "IP6": IPv6 addresses are accepted when `hostname`
       is resolved.

     Default: "ANY"
     Option type: STRING.
    */
    Hostname_resolve_to,
    /**
      Define timeout behavior for connection establishment.

      Default value of this parameter is set to "-1", which means infinite
      block. Values greater than "-1" define number of milliseconds which
      'connect' operation should wait for connection establishment.

      Default: -1.
      Option type: INTEGER.
    */
    Connect_timeout,
    /**
      Define timeout behavior when reading from the connection.

      Default value of this parameter is set to "-1", which means infinite
      block. Values greater than "-1" define number of milliseconds which
      'read' operation should wait for data.

      Default: -1.
      Option type: INTEGER.
    */
    Read_timeout,
    /**
      Define timeout behavior when writing from the connection.

      Default value of this parameter is set to "-1", which means infinite
      block. Values greater than "-1" define number of milliseconds which
      'write' operation should wait for data.

      Default: -1.
      Option type: INTEGER.
    */
    Write_timeout,
    /**
      TLS protocols permitted by the client for encrypted connections.

      The value is a comma-separated list containing one or more protocol
      names. (TLSv1,TLSv1.1,TLSv1.2 for OpenSSL, TLSv1,TLSv1.1 for yaSSL)

      Default: ""
      Option type: STRING.
    */
    Allowed_tls,
    /**
      Configure the requirements regarding SSL connection.

      It can take as arguments following string values:

      * "PREFERRED": Establish a secure (encrypted) connection if the server
        supports secure connections. Fall back to an unencrypted connection
        otherwise. This is the default value.
      * "DISABLED": Establish an unencrypted connection. This is like the
        "mysql" clients legacy --ssl=0 option or its synonyms (--skip-ssl,
        --disable-ssl).
      * "REQUIRED": Establish a secure connection if the server supports
        secure connections. The connection attempt fails if a secure
        connection cannot be established.
      * "VERIFY_CA": Like REQUIRED, but additionally verify the server TLS
        certificate against the configured Certificate Authority
        (CA) certificates. The connection attempt fails if no valid matching
        CA certificates are found.
      * "VERIFY_IDENTITY": Like VERIFY_CA, but additionally verify that the
        server certificate matches the host to which the connection is
        attempted.

      Default: "PREFERRED".
      Option type: STRING.
    */
    Ssl_mode,
    /** Path to the SSL key file in PEM format. Option type: STRING. */
    Ssl_key,
    /** Path to a file in PEM format that contains a list of trusted
    SSL certificate authorities. Option type: STRING. */
    Ssl_ca,
    /** Path to a directory that contains trusted SSL certificate authority
    certificates in PEM format. Option type: STRING. */
    Ssl_ca_path,
    /** Path to the SSL certificate file in PEM format. Option type: STRING. */
    Ssl_cert,
    /** A list of permissible ciphers to use for connection encryption.
      Option type: STRING. */
    Ssl_cipher,
    /** Path to a file containing certificate revocation lists in PEM
      format. Option type: STRING. */
    Ssl_crl,
    /** Path to a directory that contains files containing certificate
      revocation lists in PEM format. Option type: STRING. */
    Ssl_crl_path,
    /** Overwrite X Protocol authentication method:

    * "AUTO"    - let the library select authentication method
    * "MYSQL41" - do not use plain password send through network
    * "PLAIN"   - use plain password for authentication

    Default: "AUTO".
    Option type: STRING.
    */
    Authentication_method,
    /** Tells XSession what should happen when XProtocol notice handler
      didn't consume received notice.

      * TRUE - consume it.
      * FALSE - allow to return the notice by XProtocol::recv_single_message

      Default: TRUE
      Option type: BOOL
     */
    Consume_all_notices
  };

 public:
  virtual ~XSession() = default;


  /**
    Get client identifier.

    The identifier is used in/by "list_object", "kill_client" admin commands.

    @return Identifier that represents current connection/client
            in X Plugins context.
      @retval XCL_CLIENT_ID_NOT_VALID    Connection not established
      @retval != XCL_CLIENT_ID_NOT_VALID Valid client id
  */
  virtual XProtocol::Client_id client_id() const = 0;

  /**
    Get protocol layer of XSession.

    The lower layer can by used to execute custom flows, data or
    even add new behavior to already implemented flows:

    * XSession::execute_sql
    * XSession::execute_stmt
    * XSession::connect
  */
  virtual XProtocol &get_protocol() = 0;

  /**
    Modify mysqlx options.

    This method may only be called before calling `XSession::connect` method.

    @param option   option to set or modify
    @param value    assign bool value to the option

    @return Error code with description
      @retval != true     OK
      @retval == true     error occurred
  */
  virtual XError set_mysql_option(const Mysqlx_option option,
                                  const bool value) = 0;

  /**
    Modify mysqlx options.

    This method may only be called before calling `XSession::connect` method.

    @param option   option to set or modify
    @param value    assign string value to the option

    @return Error code with description
      @retval != true     OK
      @retval == true     error occurred
  */
  virtual XError set_mysql_option(const Mysqlx_option option,
                                  const std::string &value) = 0;

  /**
    Modify mysqlx options.

    This method may only be called before calling `XSession::connect` method.

    @param option   option to set or modify
    @param value    assign "C" string value to the option

    @return Error code with description
      @retval != true     OK
      @retval == true     error occurred
  */
  virtual XError set_mysql_option(const Mysqlx_option option,
                                  const char *value) = 0;

  /**
    Modify mysqlx options.

    This method may only be called before calling `XSession::connect` method.

    @param option   option to set or modify
    @param value    assign integer value to the option

    @return Error code with description
      @retval != true     OK
      @retval == true     error occurred
  */
  virtual XError set_mysql_option(const Mysqlx_option option,
                                  const int64_t value) = 0;

  /**
    Set X protocol capabilities.

    All capabilities set before calling `XSession::connect` method are
    committed to the server (other side of the connection).

    @param capability   capability to set or modify
    @param value        assign bool value to the capability

    @return Error code with description
      @retval != true     OK
      @retval == true     error occurred
  */
  virtual XError set_capability(const Mysqlx_capability capability,
                                const bool value) = 0;

  /**
    Set X protocol capabilities.

    All capabilities set before calling `XSession::connect` method are
    committed to the server (other side of the connection).

    @param capability   capability to set or modify
    @param value        assign string value to the capability

    @return Error code with description
      @retval != true     OK
      @retval == true     error occurred
  */
  virtual XError set_capability(const Mysqlx_capability capability,
                                const std::string &value) = 0;

  /**
    Set X protocol capabilities.

    All capabilities set before calling `XSession::connect` method are
    committed to the server (other side of the connection).

    @param capability   capability to set or modify
    @param value        assign "C" string value to the capability

    @return Error code with description
      @retval != true     OK
      @retval == true     error occurred
  */
  virtual XError set_capability(const Mysqlx_capability capability,
                                const char *value) = 0;

  /**
    Set X protocol capabilities.

    All capabilities set before calling `XSession::connect` method are
    committed to the server (other side of the connection).

    @param capability   capability to set or modify
    @param value        assign integer value to the capability

    @return Error code with description
      @retval != true     OK
      @retval == true     error occurred
  */
  virtual XError set_capability(const Mysqlx_capability capability,
                                const int64_t value) = 0;

  /**
    Establish and authenticate connection using TCP.

    @param host         specifies destination address as host, ipv4, ipv6 add
    @param port         specify the TCP port on which X Plugin accepts
                        connections. When the value is set to 0, the connect
                        method is going to use default MySQL X port (defined in
                        mysqlx_version.h).
    @param user         MySQL Server accounts user name
    @param pass         MySQL Server accounts user password
    @param schema       schema to be selected on start

    @return Error code with description
      @retval != true     OK
      @retval == true     error occurred
  */
  virtual XError connect(const char *host, const uint16_t port,
                         const char *user, const char *pass,
                         const char *schema) = 0;
  /**
    Establish and authenticate connection using UNIX socket

    @param socket_file  connect to the server using the unix socket file.
                        When the value is empty string or nullptr, the method
                        is going to use default UNIX socket (defined in
                        mysqlx_version.h).
    @param user         MySQL Server accounts user name
    @param pass         MySQL Server accounts user password
    @param schema       schema to be selected on start

    @return Error code with description
      @retval != true     OK
      @retval == true     error occurred
  */
  virtual XError connect(const char *socket_file, const char *user,
                         const char *pass, const char *schema) = 0;

  /**
    Reauthenticate connection.

    Reset already established session and reauthenticate using
    new MySQL Servers account.
    This method can only be called after authentication.

    @param user         MySQL Server accounts user name
    @param pass         MySQL Server accounts user password
    @param schema       schema to be selected on start

    @return Error code with description
      @retval != true     OK
      @retval == true     error occurred
  */
  virtual XError reauthenticate(const char *user, const char *pass,
                                const char *schema) = 0;

  /**
    Execute SQL.

    This method can only be called after authentication.

    @param sql             string containing SQL to be executed on the server
    @param[out] out_error  in case of error, the method is going to return error
                           code and description

    @return Object responsible for fetching "resultset/s" from the server
      @retval != nullptr  OK
      @retval == nullptr  error occurred
  */
  virtual std::unique_ptr<XQuery_result> execute_sql(
      const std::string &sql,
      XError *out_error) = 0;

  /**
    Execute statement on the server.

    This method can only be called after authentication.

    @param ns              namespace in which the statement should be executed:
                           * "sql" - interpret "stmt" string as SQL
                           * "mysqlx" - interpret "stmt" string as an admin command
    @param stmt            statement to be executed
    @param args            container with multiple values used at statement execution
    @param[out] out_error  in case of error, the method is going to return error
                           code and description
    @return Object responsible for fetching "resultset/s" from the server
      @retval != nullptr  OK
      @retval == nullptr  error occurred
  */
  virtual std::unique_ptr<XQuery_result> execute_stmt(
      const std::string &ns,
      const std::string &stmt,
      const Arguments &args,
      XError *out_error) = 0;
};
```

XProtocol
---------

``` cpp
/**
  Enum that defines result of dispatching a message or a notice
  to handler registred by user or XSession.
*/
enum class Handler_result {
  /**
    No action, dispatch the message/notice to next handler or
    return the message.
  */
  Continue,
  /**
    Message consumed. Stop dispatching the message. Requester
    is going to wait for next message/notice.
  */
  Consumed,
  /**
    Message consumed. Stop dispatching the message. Requester
    is going to receive an error.
  */
  Error
};

/**
  Enum that defines the position inside priority group
  where the handler is going to be appended.
*/
enum class Handler_position {
  Begin,
  End,
};


/**
  Enum that defines a execution priority of a handler.

  User handlers should be pushed on stack with "medium" priority. To overwrite
  behavior defined by XSession, XQuery_result, XProtocol objects, user can push
  their handler to other priorities.
*/
enum Handler_priority {
  /** Priority used by XSession object */
  Handler_priority_high = 100,
  /** Priority for handlers added by user */
  Handler_priority_medium = 200,
  /** Priority used by XSession object */
  Handler_priority_low = 300,
};

/**
  Interface defining X Protocol operations.

  It is responsible for building, serialization, deserialization of protobuf
  messages also it defines some basic X Protocol flows. The interface can be
  used for:

  * all X Protocol specific featured CRUD, pipelining, notices
  * sending messages that were recently added to proto files and 'send'
    method was not created for them
  * flows that were not implemented in XSession or XProtocol
*/
class XProtocol {
 public:
  /** Data type used on the wire for transferring Message_type_id */
  using Header_message_type_id = uint8_t;

  /** Aliases for types that represents different X Protocols message */
  using Server_message_type_id = Mysqlx::ServerMessages::Type;
  using Client_message_type_id = Mysqlx::ClientMessages::Type;

  /** Identification (representation) of notice handler placed in queue */
  using Handler_id = int;

  /** Alias for type that is able to hold X Plugins client identifier */
  using Client_id  = uint64_t;

  /** Alias for protobuf message type used by the lite or
      full version of the library */
  using Message    = HAVE_MYSQLX_FULL_PROTO(
      ::google::protobuf::Message,
      ::google::protobuf::MessageLite);

  /** Function wrapper that can be used for X Protocol notice processing */
  using Notice_handler = std::function<Handler_result (
      XProtocol *protocol,
      const bool,
      const Mysqlx::Notice::Frame::Type,
      const char *,
      const uint32_t)>;

  /** Function wrapper that can be used for X Protocol message processing. */
  using Client_message_handler = std::function<Handler_result (
      XProtocol *protocol,
      const Client_message_type_id,
      const Message &)>;
  using Server_message_handler = std::function<Handler_result (
      XProtocol *protocol,
      const Server_message_type_id,
      const Message &)>;

  using Capabilities   = ::Mysqlx::Connection::Capabilities;

 public:
  virtual ~XProtocol() = default;

  /**
    Add handler to the notice-handler list.

    Notice handlers are going to be held on a three different priority lists.
    Handler can be pushed at front or back of the list. Each notice/message
    received through this interface is going through all pushed handler
    in sequence defined by priorities and order of front/back pushes.
    Handlers are called in case when the message type is a notice and "message
    recv-handlers" didn't drop the message. When the handler returns:

    * "Handler_continue", the received notice is processed as usual.
    * "Handler_consumed", the dispatch to next handler is stopped. Received
       notice is dropped causing the receive function to wait for next notice.
    * "Handler_error", the dispatch to next handler is stopped. Received message
       is dropped and the receive functions gets and error CR_X_INTERNAL_ABORTED.

    @param handler     callback which is going to be called on each
                       notice received through current instance of XProtocol
    @param position    chooses where the handler is going to be inserted at
                       "begin" or "end" of selected priority list
    @param priority    chooses to which priority list the handler is going
                       to be added

    @return position ID of notice handler
  */
  virtual Handler_id add_notice_handler(
      Notice_handler handler,
      const Handler_position position = Handler_position::Begin,
      const Handler_priority priority = Handler_priority_medium) = 0;

  /**
    Removes a handler represented by 'id' from the notice hander container.

    @param id          id of header which should be removed
  */
  virtual void remove_notice_handler(const Handler_id id) = 0;

  /**
    Add handler to the recv-handler list.

    Received message handlers are going to be held on a three different
    priority lists. Handler can be pushed at front or back of the list.
    Each message received through this interface is going through all pushed
    handler in sequence defined by priorities and order of front/back pushes.
    Handlers are called after message deserialization. When the handler
    returns:

    * "Handler_continue", the received message is processed as usual.
    * "Handler_consumed", the dispatch to next handler is stopped, received
       message is dropped causing the receive function to wait for next message.
    * "Handler_error", the dispatch to next handler is stopped, received message
       is dropped and the receive functions gets and error CR_X_INTERNAL_ABORTED.

    @param handler     callback which is going to be called on each
                       message received through current instance of XProtocol
    @param position    chooses where the handler is going to be inserted at
                       "begin" or "end" of selected priority list
    @param priority    chooses to which priority list the handler is going
                       to be added

    @return position ID of notice handler
  */
  virtual Handler_id add_received_message_handler(
      Server_message_handler handler,
      const Handler_position position = Handler_position::Begin,
      const Handler_priority priority = Handler_priority_medium) = 0;

  /**
    Removes a handler represented by 'id' from the received
    handler container.

    @param id          id of header which should be removed
  */
  virtual void remove_received_message_handler(const Handler_id id) = 0;

  /**
    Add handler to the send-handler list.

    Send message handlers are going to be held on a three different
    priority lists. Handler can be pushed at front or back of the list.
    Each message send through this interface is going through all pushed
    handler in sequence defined by priorities and order of front/back pushes.
    Handlers are called before message serialization. Handlers return value
    is ignored.

    @param handler     callback which is going to be called on each
                       message sent through current instance of XProtocol
    @param position    chooses where the handler is going to be inserted:
                       "begin" or "end" of selected priority list
    @param priority    chooses to which priority list the handler is going
                       to be added

    @return position ID of notice handler
  */
  virtual Handler_id add_send_message_handler(
      Client_message_handler handler,
      const Handler_position position = Handler_position::Begin,
      const Handler_priority priority = Handler_priority_medium) = 0;

  /**
    Removes a handler represented by 'id' from the send
    handler container.

    @param id          id of header which should be removed
  */
  virtual void remove_send_message_handler(const Handler_id id) = 0;

  /**
    Get connection layer of XProtocol.

    The lower layer can by used do direct I/O operation on the
    socket.
  */
  virtual XConnection &get_connection() = 0;

  //
  // Methods that send or receive single message

  /**
    Read and deserialize single message.

    Message is read using XConnection, and deserialized in implementation
    of this interface. Received message before returning is going to be
    dispatched through "message handlers" and if it is a "notice" then it is
    going to be dispatched through "notice handlers". The handlers are going
    to decide what to do with the current message: ignore, allow, fail.
    When the message is ignored the  function is going to wait for next
    message.
    Following errors can occur while reading message/abort reading of the
    message:

    * I/O error from XConnection
    * timeout error from XConnection
    * error from dispatchers (notice, message)

    @param[out] out_mid    return received message identifier
    @param[out] out_error  if error occurred, this argument if going to be set
                           to its code and description

    @return Deserialized protobuf message
      @retval != nullptr   OK
      @retval == nullptr   I/O error, timeout error or dispatcher
                           error occurred
  */
  virtual std::unique_ptr<Message> recv_single_message(
      Server_message_type_id *out_mid,
      XError     *out_error) = 0;

  /**
    Receive raw payload (of X Protocol message).

    This method receives a X Protocol message which consists of 'header' and
    'payload'. The header is received first, it holds message identifier and
    payload size(buffer size), payload(content of 'buffer') is received after
    the header. The method blocks until header and whole payload is stored
    inside user provided buffer. The length of the payload is limited by
    2^32 (length field uint32) - 5 (header size).
    When the value of expression '*buffer' is set to 'nullptr', then the method
    is going to allocate the buffer for the payload. User needs to release the
    buffer by calling 'delete[]' on it.
    Message payload received using this method isn't dispatched through
    "message handler" nor "notice handlers".

    @param[out]    out_mid     message identifier of received message
    @param[in,out] buffer      buffer for the message payload
    @param[in,out] buffer_size size of the buffer, after the call it is going
                               to hold payload length

    @return Error code with description
      @retval != true     OK
      @retval == true     I/O error occurred
  */
  virtual XError recv(Header_message_type_id *out_mid,
                      uint8_t **buffer,
                      std::size_t *buffer_size) = 0;

  /**
    Deserialize X Protocol message from raw payload.

    This method deserializes the raw payload acquired by
    `XProtocol::recv` method.

    @param mid          message identifier
    @param payload      message payload
    @param payload_size message payloads size
    @param out_error    deserialization error

    @return Error code with description
      @retval != true     OK
      @retval == true     deserialization error occurred
  */
  virtual std::unique_ptr<Message> deserialize_received_message(
      const Header_message_type_id mid,
      const uint8_t *payload,
      const std::size_t payload_size,
      XError *out_error) = 0;

  /**
    Serialize and send protobuf message.

    This method builds message payload when serializing 'msg' and prepends it
    with a 'header' holding the message identifier and payload size.
    Such construction is send using XConnection interface.

    @param mid      message identifier
    @param msg      message to be serialized and sent

    @return Error code with description
      @retval != true     OK
      @retval == true     I/O error or timeout error occurred
  */
  virtual XError send(
      const Client_message_type_id mid,
      const Message &msg) = 0;

  /**
    Send the raw payload as message.

    This method sends a X Protocol message which consist from 'header' and
    'payload'.  The header is send first, it holds message identifier and
    payload size(buffer size), payload(content of 'buffer') is send after
    the header. The method blocks until header and payload is fully queued
    inside TCP stack. The length of the payload is limited by
    2^32 (length field uint32) - 5 (header size).

    @param mid      message identifier
    @param buffer   already serialized message payload
    @param length   size of the custom payload

    @return Error code with description
      @retval != true     OK
      @retval == true     I/O error occurred
  */
  virtual XError send(const Header_message_type_id mid,
                      const uint8_t *buffer,
                      const std::size_t length) = 0;

  /**
    Serialize and send protobuf message.

    @param m      message to be serialized and sent

    @return Error code with description
      @retval != true     OK
      @retval == true     I/O error occurred
  */
  virtual XError send(const Mysqlx::Session::AuthenticateStart &m) = 0;

  /**
    Serialize and send protobuf message.

    @param m      message to be serialized and sent

    @return Error code with description
      @retval != true     OK
      @retval == true     I/O error occurred
  */
  virtual XError send(const Mysqlx::Session::AuthenticateContinue &m) = 0;

  /**
    Serialize and send protobuf message.

    @param m      message to be serialized and sent

    @return Error code with description
      @retval != true     OK
      @retval == true     I/O error occurred
  */
  virtual XError send(const Mysqlx::Session::Reset &m) = 0;

  /**
    Serialize and send protobuf message.

    @param m      message to be serialized and sent

    @return Error code with description
      @retval != true     OK
      @retval == true     I/O error occurred
  */
  virtual XError send(const Mysqlx::Session::Close &m) = 0;

  /**
    Serialize and send protobuf message.

    @param m      message to be serialized and sent

    @return Error code with description
      @retval != true     OK
      @retval == true     I/O error occurred
  */
  virtual XError send(const Mysqlx::Sql::StmtExecute &m) = 0;

  /**
    Serialize and send protobuf message.

    @param m      message to be serialized and sent

    @return Error code with description
      @retval != true     OK
      @retval == true     I/O error occurred
  */
  virtual XError send(const Mysqlx::Crud::Find &m) = 0;

  /**
    Serialize and send protobuf message.

    @param m      message to be serialized and sent

    @return Error code with description
      @retval != true     OK
      @retval == true     I/O error occurred
  */
  virtual XError send(const Mysqlx::Crud::Insert &m) = 0;

  /**
    Serialize and send protobuf message.

    @param m      message to be serialized and sent

    @return Error code with description
      @retval != true     OK
      @retval == true     I/O error occurred
  */
  virtual XError send(const Mysqlx::Crud::Update &m) = 0;

  /**
    Serialize and send protobuf message.

    @param m      message to be serialized and sent

    @return Error code with description
      @retval != true     OK
      @retval == true     I/O error occurred
  */
  virtual XError send(const Mysqlx::Crud::Delete &m) = 0;

  /**
    Serialize and send protobuf message.

    @param m      message to be serialized and sent

    @return Error code with description
      @retval != true     OK
      @retval == true     I/O error occurred
  */
  virtual XError send(const Mysqlx::Crud::CreateView &m) = 0;

  /**
    Serialize and send protobuf message.

    @param m      message to be serialized and sent

    @return Error code with description
      @retval != true     OK
      @retval == true     I/O error occurred
  */
  virtual XError send(const Mysqlx::Crud::ModifyView &m) = 0;

  /**
    Serialize and send protobuf message.

    @param m      message to be serialized and sent

    @return Error code with description
      @retval != true     OK
      @retval == true     I/O error occurred
  */
  virtual XError send(const Mysqlx::Crud::DropView &m) = 0;

  /**
    Serialize and send protobuf message.

    @param m      message to be serialized and sent

    @return Error code with description
      @retval != true     OK
      @retval == true     I/O error occurred
  */
  virtual XError send(const Mysqlx::Expect::Open &m) = 0;

  /**
    Serialize and send protobuf message.

    @param m      message to be serialized and sent

    @return Error code with description
      @retval != true     OK
      @retval == true     I/O error occurred
  */
  virtual XError send(const Mysqlx::Expect::Close &m) = 0;

  /**
    Serialize and send protobuf message.

    @param m      message to be serialized and sent

    @return Error code with description
      @retval != true     OK
      @retval == true     I/O error occurred
  */
  virtual XError send(const Mysqlx::Connection::CapabilitiesGet &m) = 0;

  /**
    Serialize and send protobuf message.

    @param m      message to be serialized and sent

    @return Error code with description
      @retval != true     OK
      @retval == true     I/O error occurred
  */
  virtual XError send(const Mysqlx::Connection::CapabilitiesSet &m) = 0;

  /**
    Serialize and send protobuf message.

    @param m      message to be serialized and sent

    @return Error code with description
      @retval != true     OK
      @retval == true     I/O error occurred
  */
  virtual XError send(const Mysqlx::Connection::Close &m) = 0;

  /*
    Methods that execute different message flows
    with the server
  */

  /**
    Get an object that is capable of reading resultsets.

    Create and return an object without doing I/O operations.

    @return Object responsible for fetching "resultset/s" from the server
      @retval != nullptr  OK
      @retval == nullptr  error occurred
  */
  virtual std::unique_ptr<XQuery_result> recv_resultset() = 0;

  /**
    Get an object that is capable of reading resultsets.

    Create and return an object which already fetched metadata.
    If server returns an error or an I/O error occurred then
    the result is "nullptr".

    @param[out] out_error  in case of error, the method is going to return error
                           code and description

    @return Object responsible for fetching "resultset/s" from the server
      @retval != nullptr  OK
      @retval == nullptr  error occurred
  */
  virtual std::unique_ptr<XQuery_result> recv_resultset(XError *out_error) = 0;

  /**
    Read "Mysqlx.Ok" message.

    Expect to receive "Ok" message.
    * in case of any other message return out of sync error
    * in case "Mysqlx.Error" message translate it to "XError"
    "Ok" message is used in several different situations like: setting
    capabilities, creating views, expectations.

    @return Error code with description
      @retval != true     Received OK message
      @retval == true     I/O error, timeout error, dispatch error
                           or received "Mysqlx.Error" message
   */
  virtual XError recv_ok() = 0;

  /**
    Execute session closing flow.

    Send "Mysqlx::Session::Close" message and expect successful confirmation
    from the X Plugin by reception of "Mysqlx::Ok". Synchronization errors and
    "Mysqlx::Error" are returned through return values.

    @return Error code with description
      @retval != true     Received OK message
      @retval == true     I/O error, timeout error, dispatch error
                           or received "Mysqlx.Error" message
   */
  virtual XError execute_close() = 0;

  /**
    Send custom message and expect resultset as response.

    @param mid             id of the message to be serialized
    @param msg             message to be serialized and sent
    @param[out] out_error  in case of error, the method is going to return error
                           code and description

    @return Object responsible for fetching "resultset/s" from the server
      @retval != nullptr  OK
      @retval == nullptr  I/O error, timeout error, dispatch error
                          or received "Mysqlx.Error" message
  */
  virtual std::unique_ptr<XQuery_result> execute_with_resultset(
      const Client_message_type_id mid,
      const Message &msg,
      XError *out_error) = 0;

  /**
    Send statement execute and expect resultset as response.

    @param msg             "StmtExecute" message to be serialized and sent
    @param[out] out_error  in case of error, the method is going to return error
                           code and description
    @return Object responsible for fetching "resultset/s" from the server
      @retval != nullptr  OK
      @retval == nullptr  I/O error, timeout error, dispatch error
                          or received "Mysqlx.Error" message
  */
  virtual std::unique_ptr<XQuery_result> execute_stmt(
      const Mysqlx::Sql::StmtExecute &msg,
      XError *out_error) = 0;

  /**
    Send crud find and expect resultset as response.

    @param msg             "Find" message to be serialized and sent
    @param[out] out_error  in case of error, the method is going to return error
                           code and description

    @return Object responsible for fetching "resultset/s" from the server
      @retval != nullptr  OK
      @retval == nullptr  I/O error, timeout error, dispatch error
                          or received "Mysqlx.Error" message
  */
  virtual std::unique_ptr<XQuery_result> execute_find(
      const Mysqlx::Crud::Find &msg,
      XError *out_error) = 0;

  /**
    Send crud update and expect resultset as response.

    @param msg             "Update" message to be serialized and sent
    @param[out] out_error  in case of error, the method is going to return error
                           code and description

    @return Object responsible for fetching "resultset/s" from the server
      @retval != nullptr  OK
      @retval == nullptr  I/O error, timeout error, dispatch error
                          or received "Mysqlx.Error" message
  */
  virtual std::unique_ptr<XQuery_result> execute_update(
      const Mysqlx::Crud::Update &msg,
      XError *out_error) = 0;

  /**
    Send crud insert and expect resultset as response.

    @param msg             "Insert" message to be serialized and sent
    @param[out] out_error  in case of error, the method is going to return error
                           code and description

    @return Object responsible for fetching "resultset/s" from the server
      @retval != nullptr  OK
      @retval == nullptr  I/O error, timeout error, dispatch error
                          or received "Mysqlx.Error" message
  */
  virtual std::unique_ptr<XQuery_result> execute_insert(
      const Mysqlx::Crud::Insert &msg,
      XError *out_error) = 0;

  /**
    Send crud delete and expect resultset as response.

    @param msg             "Delete" message to be serialized and sent
    @param[out] out_error  in case of error, the method is going to return error
                           code and description
    @return Object responsible for fetching "resultset/s" from the server
      @retval != nullptr  OK
      @retval == nullptr  I/O error, timeout error, dispatch error
                          or received "Mysqlx.Error" message
  */
  virtual std::unique_ptr<XQuery_result> execute_delete(
      const Mysqlx::Crud::Delete &msg,
      XError *out_error) = 0;

  /**
    Send "CapabilitiesGet" and expect Capabilities as response.

    @param[out] out_error  in case of error, the method is going to return error
                           code and description

    @return X Protocol message containing capabilities available exposed
            by X Plugin
      @retval != nullptr  OK
      @retval == nullptr  I/O error, timeout error, dispatch error
                          or received "Mysqlx.Error" message
  */
  virtual std::unique_ptr<Capabilities> execute_fetch_capabilities(
      XError *out_error) = 0;

  /**
    Execute "CapabilitiesSet" and expect "Ok" as response.

    @param capabilities   message containing cababilities to be set

    @return Error code with description
      @retval != true     OK
      @retval == true     I/O error, timeout error, dispatch error
                          or received "Mysqlx.Error" message
  */
  virtual XError execute_set_capability(
      const Mysqlx::Connection::CapabilitiesSet &capabilities) = 0;

  /**
    Execute authentication flow

    @param user    MySQL Server account name
    @param pass    MySQL Server accounts authentication string
    @param schema  schema which should be "used"
    @param method  X Protocol authentication method, for example:
                  "PLAIN", "MYSQL41"

    @return Error code with description
      @retval != true     OK
      @retval == true     I/O error, timeout error, dispatch error
                          or received "Mysqlx.Error" message
  */
  virtual XError execute_authenticate(const std::string &user,
                                      const std::string &pass,
                                      const std::string &schema,
                                      const std::string &method = "") = 0;
};
```

XConnection
-----------

``` cpp
//**
  'Enum' that defines allowed version of Internet Protocol.

  The value defines which "socket-proto" must be used by implementer
  of XConnection interface also tell the resolver which IP addresses
  are allowed when resolving hostname to IP address.
*/
enum class Internet_protocol {
  Any = 0,
  V4,
  V6,
};

/** 'Enum' that defines how the network connection should be closed. */
enum class Shutdown_type {
  Send,
  Recv,
  Both
};

/**
 Interface defining network layer.

 This is the lowest layer on which XSession or XProtocol implementers
 can operate on. It defines basic blocking I/O operations on a connection.
 Additionally it handles all data stream encoding/decoding (for example SSL).
*/
class XConnection {
 public:
  /** Interface describing the connection state. */
  class State {
   public:
    virtual ~State() = default;

    /** Check if SSL was configured */
    virtual bool is_ssl_configured() const = 0;

    /** Check if SSL layer works */
    virtual bool is_ssl_activated() const = 0;

    /** Check connection state */
    virtual bool is_connected() const = 0;

    /** Get version of the SSL protocol used */
    virtual std::string get_ssl_version() const = 0;

    /** Get cipher used by SSL layer */
    virtual std::string get_ssl_cipher() const = 0;
  };

 public:
  virtual ~XConnection() = default;

  /**
    Connect to UNIX socket.

    Connect is going to block until:

    * operation completed successfully
    * I/O error occurred
    * timeout occurred (@ref XSession::Mysqlx_option::Connect_timeout)

    @param unix_socket   UNIX socket file created by X Plugin

    @return Object holding result of the operation
      @retval == true   error occurred
      @retval == false  operation successful
  */
  virtual XError connect_to_localhost(const std::string &unix_socket) = 0;

  /**
    Connect to host through TCP/IP.

    Connect is going to block until:

    * operation completed successfully
    * I/O error occurred
    * timeout occurred

    @param host     hostname or IPv4 or IPv6 address
    @param port     TCP port used by X Plugin (running with X Protocol)
    @param ip_mode  defines allowed IP version

    @return Object holding result of the operation
      @retval == true   error occurred
      @retval == false  operation successful
  */
  virtual XError connect(const std::string &host, const uint16_t port,
                         const Internet_protocol ip_mode) = 0;

  /**
    Get the connections file descriptor (socket).

    Please be aware that after enabling SSL the data could be fetched inside
    SSLs buffers. Thus checking for activity by executing 'select'
    could lead to infinite wait. Similar problem can occur after enabling
    'timeouts' by calling 'set_read_timeout' or 'set_write_timeout'.

    @return Socket - file descriptor
  */
  virtual my_socket get_socket_fd() = 0;

  /**
    Activate TLS on the lowest layer.

    This method activates SSL and validates certificate authority when
    "SSL mode" is set to:

    * VERIFY_CA
    * VERIFY_IDENTITY

    Other SSL checks are going to be done by layer that calls this method.

    @return Object holding result of the operation
      @retval == true   error occurred
      @retval == false  operation successful
    */
  virtual XError activate_tls() = 0;

  /**
    Shutdown the connection.

    @param how_to_shutdown   define which part of the socket
                             should be closed (sending/receiving)

    @return Object holding result of the operation
      @retval == true   error occurred
      @retval == false  operation successful
   */
  virtual XError shutdown(const Shutdown_type how_to_shutdown) = 0;

  /**
    Write the data.

    Write operation is going to block until expected number of bytes has been
    send on TCP stack. In case when the write-timeout was set, the operation
    can block at most the given number of seconds.

    If the SSL is enabled the data is first encoded and then sent.

    @param data          payload to be sent
    @param data_length   size of the payload

    @return Object holding result of the operation
      @retval == true   error occurred
      @retval == false  operation successful
   */
  virtual XError write(const uint8_t *data, const std::size_t data_length) = 0;

  /**
    Read the data.

    Read operation is going to block until expected number of bytes has been
    received. In case when the read-timeout was set, the operation can block
    at most the given number of seconds.

    If the SSL is enabled the data is first decoded and then put into receive
    buffer.

    @param data          buffer which should receive/get data
    @param data_length   number of bytes which must be read from the
                         connection

    @return Object holding result of the operation
      @retval == true   error occurred
      @retval == false  operation successful
   */
  virtual XError read(uint8_t *data, const std::size_t data_length) = 0;

  /** Define timeout behavior when reading from the connection:

  @param deadline_seconds - values greater than 0, set number of seconds which
                            read operation can block
                          - value set to zero, do non blocking op
                          - value less than 0, do blocking op */
  virtual XError set_read_timeout(const int deadline_seconds) = 0;

  /** Define timeout behavior when writing from the connection:

  @param deadline_seconds - values greater than 0, set number of seconds which
                            write operation can block
                          - value set to zero, do non blocking op
                          - value less than 0, do blocking op */
  virtual XError set_write_timeout(const int deadline_seconds) = 0;

  /** Close connection. */
  virtual void close() = 0;

  /** Get state of the connection. */
  virtual const State &state() = 0;
};
```

XQuery_result
-------------

``` cpp
/**
  Interface responsible for fetching 'resultsets'.

  The interface defines methods that correspond to X Protocol message flow
  which transmits multiple 'resultsets':

      loop has more resultsets
        group resultset
          loop has more columns
            server --> client: ColumnMetaData
          end
          loop has more rows
            server --> client: Row
          end
        end
        alt has more resultsets
          server --> client: FetchDoneMoreResultsets
        end
      end
      loop has more OUT-paramsets
        server --> client: FetchDoneMoreOutParams
        group resultset
          loop has more columns
            server --> client: ColumnMetaData
          end
          loop has more rows
            server --> client: Row
          end
        end
      end
      server --> client: FetchDone

  While designing it there was an assumption that the implementation may not
  fetch/buffers row data. Because of it some return containers may "grow" with
  the realization of the X Protocol flow.
*/
class XQuery_result {
 public:
  using Warning  = ::Mysqlx::Notice::Warning;
  using Row      = ::Mysqlx::Resultset::Row;
  using Metadata = std::vector<Column_metadata>;
  using Warnings = std::vector<Warning>;

 public:
  virtual ~XQuery_result() = default;

  /**
    Get last insert identifier.

    If last insert identifier was generated by the query then
    it is going to be send in few last messages of the statement
    execution flow thus the value can be checked after last 'resultset'
    was received (@ref next_resultset returned false). Before this point
    the method is going to return 'false'.

    @param[out] out_last_id   returns last insert identifier

    @return Information if the last insert identifier is defined for
            last 'resultset'
      @retval == true    last insert id is defined
      @retval == false   last insert id is not defined
  */
  virtual bool try_get_last_insert_id(uint64_t *out_last_id) const = 0;

  /**
    Get number of affected rows.

    If affected row count was generated by the query then
    it is going to be send in few last messages of the statement
    execution flow thus the value can be checked after last 'resultset'
    was received (@ref next_resultset returned false). Before this point
    the method is going to return 'false'.

    @param[out] out_affected_number   returns last insert identifier

    @return Information if the affected number of rows is defined for
            last 'resultset'
      @retval == true    number of affected rows is defined
      @retval == false   number of affected rows is not defined
  */
  virtual bool try_get_affected_rows(uint64_t *out_affected_number) const = 0;

  /**
    Get message returned for the server.

    If the message was generated by the query then it is going to be send
    in few last messages of the statement execution flow thus the value
    can be checked after last 'resultset' was received (@ref next_resultset
    returned false). Before this point the method is going to return 'false'.

    @param[out] out_message   returns message

    @return Information if the message is defined for last 'resultset'
      @retval == true    number of affected rows is defined
      @retval == false   number of affected rows is not defined
  */
  virtual bool try_get_info_message(std::string *out_message) const = 0;

  /**
    Get column information for current 'resultset'.

    Returns column information in case when there was no error
    earlier and fetch of metadata was successful.
    The method doesn't need to be called before call to "get_next_row*",
    it is done implicitly in that case and the metadata-s are cached.

    @param[out] out_error   return error in case the fetch operation fails
                            or last error. To omit the value the method
                            accepts "nullptr" value.

    @return Container holding Column_metadata for all columns
  */
  virtual const Metadata &get_metadata(XError *out_error = nullptr) = 0;

  /**
    Get warning generated for current 'resultset'.

    Warnings are generated by X Plugin or SQL executed on the server
    and are forwarded to the client through X Protocol notices.
    Number of warnings is going to increase with flow realization and
    may be generated while serializing 'row', 'field'.

    @return Container holding warnings generated until now
  */
  virtual const Warnings &get_warnings() = 0;

  /**
    Get next row of the current 'resultset'.

    @param[out] out_row    returns a row pointer from the internal storage,
                           if the value is 'nullptr' then row is skipped
    @param[out] out_error  returns an error when fetching of row failed
                           or last error

    @return Result of row fetching
      @retval == true   out_row contains a valid row
      @retval == false  there are no rows to fetch in current 'resultset' or
                        an error occurred
  */
  virtual bool get_next_row(const XRow **out_row, XError *out_error) = 0;

  /**
    Get next row of the current 'resultset'.

    @param[out] out_error  returns an error when fetching of row failed
                           or last error

    @return Result of row fetching
      @retval != nullptr  pointer to fetched row owned by this interface
      @retval == nullptr  there are no rows to fetch in current 'resultset' or
                          an error occurred
  */
  virtual const XRow *get_next_row(XError *out_error = nullptr) = 0;

  /**
    Get next row of the current 'resultset' as X Protocol message.

    @param[out] out_error  returns an error when fetching of row failed
                           or last error

    @return Result of row fetching
      @retval != nullptr  pointer to fetched X Protocol message
      @retval == nullptr  there are no rows to fetch in current 'resultset' or
                          an error occurred
  */
  virtual std::unique_ptr<Row> get_next_row_raw(
      XError *out_error = nullptr) = 0;

  /**
    Move to next 'resultset'.

    Method checks if the executed query generated multiple/next "resultset/s".
    It returns "true" if the next "resultset" is present and can be read
    by calling once again by methods like @ref get_metadata,
    @ref get_warnings, @ref get_next_row, @ref get_next_row_raw.
    In case of "false", user should check out_error to see if the cause of
    stopping was an error or an end of resultsets.
    Using this method user can skip in middle of current resultset and
    go to next one or even skip whole resultset.

    @param[out] out_error   returns an error if it occurred while
                            skipping/moving message flow to the next
                            'resultset' or last error

    @return Information if user is able to read next 'resultset'
      @retval == true       next 'resultset' is available
      @retval == false      all 'resultset' received or an error occur
  */
  virtual bool next_resultset(XError *out_error) = 0;


  /**
    Check if there is a 'resultset'.

    Method tries to fetch 'resultset' metadata to check if there
    is a resultset.

    @param[out] out_error   returns an error if it occurred while
                            checking the resultset
  */
  virtual bool has_resultset(XError *out_error = nullptr) = 0;
};
```

XRow
----

``` cpp
/**
  Column types supported by the client library
*/
enum class Column_type {
  SINT,
  UINT,
  DOUBLE,
  FLOAT,
  BYTES,
  TIME,
  DATETIME,
  SET,
  ENUM,
  BIT,
  DECIMAL
};

/**
  Structure holding column information.

  This structure is a compact version of "Mysqlx::Resultset::ColumnMetaData".
*/
struct Column_metadata {
    Column_type type;
    std::string name;
    std::string original_name;
    std::string table;
    std::string original_table;
    std::string schema;
    std::string catalog;

    uint64_t collation;
    uint32_t fractional_digits;
    uint32_t length;
    uint32_t flags;
    uint32_t content_type;
};

/**
  Interface wrapping "::Mysqlx::Resultset::Row" message.

  "Row" messages holds fields as row data, which must be
  converted to "C++" types using decoders present in
  "xcl::row_decoder" namespace. The interface encapsulates
  "Row" messages and "row_decoder" functions to make
  easy in use interface.
*/
class XRow {
 public:
  /** Alias for Row protobuf message. */
  using Row        = ::Mysqlx::Resultset::Row;
  /** Alias for set of strings used for MySQL "SET" type. */
  using String_set = std::set<std::string>;

 public:
  virtual ~XRow() = default;

  /** Validate the data placed in XRow. */
  virtual bool valid() const = 0;

  /** Get number of fields in row. */
  virtual int32_t get_number_of_fields() const = 0;

  /**
    Check field if its empty.

    @param field_index    index of the field/column to check

    @return Result of accessing the data
      @retval == true     field contains value
      @retval == false     field is empty
  */
  virtual bool is_null(const int32_t field_index) const = 0;

  /**
    Get field data as int64_t value.

    Method validates if column type stored in Column_metadata is equal
    to "Column_type::SINT" also if the field "raw" data contains enough
    bytes for conversion to int64. Getter is going to fail when one of
    those checks fails.

    @param field_index    index of the field/column to get
    @param[out] out_data  int32_t result. "null" pointer is accepted,
                          conversion is done nevertheless but returning
                          the result is omitted.

    @return Result of accessing the data
      @retval == true     OK
      @retval == false    getter failed
  */
  virtual bool get_int64(const int32_t field_index,
                         int64_t *out_data) const = 0;

  /**
    Get field data as uint64_t value.

    Method validates if column type stored in Column_metadata is equal
    to "Column_type::UINT" also if the field "raw" data contains enough
    bytes for conversion to uint64. Getter is going to fail when one of
    those checks fails.

    @param field_index    index of the field/column to get
    @param[out] out_data  uint32_t result. "null" pointer is accepted,
                          conversion is done nevertheless but returning
                          the result is omitted.

    @return Result of accessing the data
      @retval == true     OK
      @retval == false    getter failed
  */
  virtual bool get_uint64(const int32_t field_index,
                          uint64_t *out_data) const = 0;

  /**
    Get field data as double value.

    Method validates if column type stored in Column_metadata is equal
    to "Column_type::DOUBLE" also if the field "raw" data contains enough
    bytes for conversion to double. Getter is going to fail when one of
    those checks fails.

    @param field_index    index of the field/column to get
    @param[out] out_data  double result. "null" pointer is accepted, conversion
                          is done nevertheless but returning the result
                          is omitted.

    @return Result of accessing the data
      @retval == true     OK
      @retval == false    getter failed
  */
  virtual bool get_double(const int32_t field_index,
                          double *out_data) const = 0;

  /**
    Get field data as float value.

    Method validates if column type stored in Column_metadata is equal
    to "Column_type::FLOAT" also if the field "raw" data contains enough
    bytes for conversion to float. Getter is going to fail when one of
    those checks fails.

    @param field_index    index of the field/column to get
    @param[out] out_data  float result. "null" pointer is accepted, conversion
                          is done nevertheless but returning the result
                          is omitted.

    @return Result of accessing the data
      @retval == true     OK
      @retval == false    getter failed
  */
  virtual bool get_float(const int32_t field_index, float *out_data) const = 0;

  /**
    Get field data as string value.

    Method validates if column type stored in Column_metadata is equal
    to "Column_type::BYTES" also if the field "raw" data contains enough
    bytes for conversion to string. Getter is going to fail when one of
    those checks fails.

    @param field_index    index of the field/column to get
    @param[out] out_data  string result. "null" pointer is accepted, conversion
                          is done nevertheless but returning the result
                          is omitted.

    @return Result of accessing the data
      @retval == true     OK
      @retval == false    getter failed
  */
  virtual bool get_string(const int32_t field_index,
                          std::string *out_data) const = 0;

  /**
    Get field data as "c" string value.

    Method validates if column type stored in Column_metadata is equal
    to "Column_type::BYTES" also if the field "raw" data contains enough
    bytes for conversion to "c" string. Getter is going to fail when one of
    those checks fails.

    @param field_index          index of the field/column to get
    @param[out] out_data        "c" string result. "null" pointer is accepted,
                                conversion is done nevertheless but returning
                                the result is omitted.
    @param[out] out_data_length length of the returned "c" string

    @return Result of accessing the data
      @retval == true     OK
      @retval == false    getter failed
  */
  virtual bool get_string(const int32_t field_index,
                          const char **out_data,
                          size_t *out_data_length) const = 0;

  /**
    Get field data as decimal value.

    Method validates if column type stored in Column_metadata is equal
    to "Column_type::DECIMAL" also if the field "raw" data contains enough
    bytes for conversion to Decimal. Getter is going to fail when one of
    those checks fails.

    @param field_index    index of the field/column to get
    @param[out] out_data  decimal result. "null" pointer is accepted,
                          conversion is done nevertheless but returning
                          the result is omitted.

    @return Result of accessing the data
      @retval == true     OK
      @retval == false    getter failed
  */
  virtual bool get_decimal(const int32_t field_index,
                           Decimal *out_data) const = 0;

  /**
    Get field data as enum value as string.

    Method validates if column type stored in Column_metadata is equal
    to "Column_type::ENUM" also if the field "raw" data contains enough
    bytes for conversion to string. Getter is going to fail when one of
    those checks fails.

    @param field_index    index of the field/column to get
    @param[out] out_data  string result. "null" pointer is accepted,
                          conversion is done nevertheless but returning
                          the result is omitted.

    @return Result of accessing the data
      @retval == true     OK
      @retval == false    getter failed
  */
  virtual bool get_enum(const int32_t field_index,
                        std::string *out_data) const = 0;

  /**
    Get field data as enum value as "c" string.

    Method validates if column type stored in Column_metadata is equal
    to "Column_type::ENUM" also if the field "raw" data contains enough
    bytes for conversion to "c" string. Getter is going to fail when one of
    those checks fails.

    @param field_index          index of the field/column to get
    @param[out] out_data        "c" string result. "null" pointer is accepted,
                                conversion is done nevertheless but returning
                                the result is omitted
    @param[out] out_data_length length of the returned "c" string

    @return Result of accessing the data
      @retval == true     OK
      @retval == false    getter failed
  */
  virtual bool get_enum(const int32_t field_index,
                        const char **out_data,
                        size_t *out_data_length) const = 0;

  /**
    Get field data as Time value.

    Method validates if column type stored in Column_metadata is equal
    to "Column_type::TIME" also if the field "raw" data contains enough
    bytes for conversion to Time. Getter is going to fail when one of
    those checks fails.

    @param field_index    index of the field/column to get
    @param[out] out_data  Time result. "null" pointer is accepted,
                          conversion is done nevertheless but returning
                          the result is omitted.

    @return Result of accessing the data
      @retval == true     OK
      @retval == false    getter failed
  */
  virtual bool get_time(const int32_t field_index, Time *out_data) const = 0;

  /**
    Get field data as DataTime value.

    Method validates if column type stored in Column_metadata is equal
    to "Column_type::DATETIME" also if the field "raw" data contains enough
    bytes for conversion to DateTime. Getter is going to fail when one of
    those checks fails.

    @param field_index    index of the field/column to get
    @param[out] out_data  DataTime result. "null" pointer is accepted,
                          conversion is done nevertheless but returning
                          the result is omitted.

    @return Result of accessing the data
      @retval == true     OK
      @retval == false    getter failed
  */
  virtual bool get_datetime(const int32_t field_index,
                            DateTime *out_data) const = 0;

  /**
    Get field data as set of strings value.

    Method validates if column type stored in Column_metadata is equal
    to "Column_type::SET" also if the field "raw" data contains enough
    bytes for conversion to set<string>. Getter is going to fail when
    one of those checks fails.

    @param field_index    index of the field/column to get
    @param[out] out_data  set<string> result. "null" pointer is accepted,
                          conversion is done nevertheless but returning
                          the result is omitted.

    @return Result of accessing the data
      @retval == true     OK
      @retval == false    getter failed
  */
  virtual bool get_set(const int32_t field_index,
                       String_set *out_data) const = 0;

  /**
    Get field data as boolean value.

    Method validates if column type stored in Column_metadata is equal
    to "Column_type::BIT" also if the field "raw" data contains enough
    bytes for conversion to bool. Getter is going to fail when
    one of those checks fails.

    @param field_index    index of the field/column to get
    @param[out] out_data  bool result. "null" pointer is accepted,
                          conversion is done nevertheless but returning
                          the result is omitted.

    @return Result of accessing the data
      @retval == true     OK
      @retval == false    getter failed
  */
  virtual bool get_bit(const int32_t field_index, bool *out_data) const = 0;

  /**
    Get field data as uint64_t boolean.

    Method validates if column type stored in Column_metadata is equal
    to "Column_type::BIT" also if the field "raw" data contains enough
    bytes for conversion to uint64. Getter is going to fail when
    one of those checks fails.

    @param field_index    index of the field/column to get
    @param[out] out_data  uint64_t result. "null" pointer is accepted,
                          conversion is done nevertheless but returning
                          the result is omitted.

    @return Result of accessing the data
      @retval == true     OK
      @retval == false    getter failed
  */
  virtual bool get_bit(const int32_t field_index, uint64_t *out_data) const = 0;

  /**
    Get field data and convert it to string.

    In case of null value, the method returns "null" string in all other
    cases its is going to call one of: get_int64_t, get_uint64_t, get_bit,
    get_string, get_set... based on  column type. The resulting "C++"
    type is going to be converted to string (out_data).
    If "field" to "C++ data type" conversion is going to fail the method
    is going fail.

    @param field_index    index of the field/column to convert
    @param[out] out_data  field converted to string. "null" pointer
                          is accepted, conversion is done still returning
                          the result is omitted.

    @return Result of accessing the data
      @retval == true     OK
      @retval == false    getter failed
  */
  virtual bool get_field_as_string(const int32_t field_index,
                                   std::string *out_data) const  = 0;
};
```