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; }; ```
Copyright (c) 2000, 2024, Oracle Corporation and/or its affiliates. All rights reserved.