mirror of https://github.com/boostorg/mysql.git
Multi-function operations
Added proper support for multi-function operations, segregating the API in single-function and multi-function operations. Removed resultset as I/O object, the read_xxx owning functions and read_all. Unit test infrastructure for async testing. Reference types lifetime fixes. Removed connection::valid. Fixed a serialization bug for statements with no parameters. Binary protocol strings now use the type recommended by MySQL. Refactored Jamfile to match best practices. Updated description in libraries.json. Close #82 Close #81 Close #73 Close #59 Close #58 Close #53 Close #41 Close #22
This commit is contained in:
parent
a3c9844ef3
commit
a975273490
|
@ -15,6 +15,7 @@ def _image(name):
|
|||
|
||||
def _b2_command(source_dir, toolset, cxxstd, variant, stdlib='native', address_model='64', server_host='localhost'):
|
||||
return 'python tools/ci.py ' + \
|
||||
'--clean=1 ' + \
|
||||
'--build-kind=b2 ' + \
|
||||
'--source-dir="{}" '.format(source_dir) + \
|
||||
'--toolset={} '.format(toolset) + \
|
||||
|
@ -28,6 +29,7 @@ def _b2_command(source_dir, toolset, cxxstd, variant, stdlib='native', address_m
|
|||
def _cmake_command(source_dir, build_shared_libs=0, valgrind=0, coverage=0, generator='Ninja', db='mysql8', server_host='localhost'):
|
||||
return 'python tools/ci.py ' + \
|
||||
'--build-kind=cmake ' + \
|
||||
'--clean=1 ' + \
|
||||
'--generator="{}" '.format(generator) + \
|
||||
'--source-dir="{}" '.format(source_dir) + \
|
||||
'--build-shared-libs={} '.format(build_shared_libs) + \
|
||||
|
@ -194,7 +196,7 @@ def docs():
|
|||
"image": _image('build-docs'),
|
||||
"pull": "if-not-exists",
|
||||
"commands": [
|
||||
'python tools/ci.py --build-kind=docs --source-dir=$(pwd)'
|
||||
'python tools/ci.py --build-kind=docs --clean=1 --source-dir=$(pwd)'
|
||||
]
|
||||
}]
|
||||
}
|
||||
|
|
120
Jamfile
120
Jamfile
|
@ -1,120 +0,0 @@
|
|||
#
|
||||
# Copyright (c) 2019-2020 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
#
|
||||
# Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
|
||||
import os ;
|
||||
import path ;
|
||||
|
||||
project /boost/mysql ;
|
||||
|
||||
# System libraries
|
||||
if [ os.name ] = NT
|
||||
{
|
||||
local OPENSSL_ROOT_ENV = [ os.environ OPENSSL_ROOT ] ;
|
||||
local OPENSSL_ROOT = "" ;
|
||||
if $(OPENSSL_ROOT_ENV)
|
||||
{
|
||||
OPENSSL_ROOT = $(OPENSSL_ROOT_ENV) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
OPENSSL_ROOT = "C:/OpenSSL" ;
|
||||
}
|
||||
project
|
||||
: requirements
|
||||
<include>$(OPENSSL_ROOT)/include
|
||||
<variant>debug:<library-path>$(OPENSSL_ROOT)/lib
|
||||
<target-os>windows<variant>debug:<library-path>$(OPENSSL_ROOT)/debug/lib
|
||||
<variant>release:<library-path>$(OPENSSL_ROOT)/lib
|
||||
;
|
||||
|
||||
if [ path.exists $(OPENSSL_ROOT)/lib/libssl.lib ]
|
||||
{
|
||||
echo "OpenSSL > 1.1.0. Including libssl" ;
|
||||
lib ssl : : <target-os>windows <name>libssl ;
|
||||
}
|
||||
else if [ path.exists $(OPENSSL_ROOT)/lib/ssleay32.lib ]
|
||||
{
|
||||
echo "OpenSSL < 1.1.0. Including ssleay32" ;
|
||||
lib ssl : : <target-os>windows <name>ssleay32 ;
|
||||
}
|
||||
else
|
||||
{
|
||||
lib ssl : : <link>shared ;
|
||||
}
|
||||
|
||||
if [ path.exists $(OPENSSL_ROOT)/lib/libcrypto.lib ]
|
||||
{
|
||||
echo "OpenSSL > 1.1.0. Including libcrypto" ;
|
||||
lib crypto : : <target-os>windows <name>libcrypto ;
|
||||
}
|
||||
else if [ path.exists $(OPENSSL_ROOT)/lib/libeay32.lib ]
|
||||
{
|
||||
echo "OpenSSL < 1.1.0. Including libeay32" ;
|
||||
lib crypto : : <target-os>windows <name>libeay32 ;
|
||||
}
|
||||
else
|
||||
{
|
||||
lib crypto : : <link>shared ;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
local OPENSSL_ROOT = [ os.environ OPENSSL_ROOT ] ;
|
||||
if $(OPENSSL_ROOT)
|
||||
{
|
||||
project
|
||||
: requirements
|
||||
<include>$(OPENSSL_ROOT)/include
|
||||
<library-path>$(OPENSSL_ROOT)/lib
|
||||
;
|
||||
|
||||
}
|
||||
lib ssl : : <link>shared ;
|
||||
lib crypto : : <link>shared ;
|
||||
}
|
||||
|
||||
# Requirements to use across targets
|
||||
local requirements =
|
||||
<include>../include
|
||||
<define>BOOST_ALL_NO_LIB=1
|
||||
<define>BOOST_ASIO_NO_DEPRECATED=1
|
||||
<define>BOOST_ASIO_DISABLE_BOOST_ARRAY=1
|
||||
<define>BOOST_ASIO_DISABLE_BOOST_BIND=1
|
||||
<define>BOOST_ASIO_DISABLE_BOOST_DATE_TIME=1
|
||||
<define>BOOST_ASIO_DISABLE_BOOST_REGEX=1
|
||||
<define>BOOST_ASIO_HAS_DEFAULT_FUNCTION_TEMPLATE_ARGUMENTS=1
|
||||
<define>BOOST_COROUTINES_NO_DEPRECATION_WARNING=1
|
||||
<define>BOOST_ALLOW_DEPRECATED_HEADERS=1
|
||||
<toolset>msvc:<cxxflags>"/bigobj /Zc:__cplusplus"
|
||||
<toolset>msvc-14.1:<cxxflags>"/permissive-"
|
||||
<toolset>msvc-14.2:<cxxflags>"/permissive-"
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1
|
||||
<toolset>msvc:<define>_SILENCE_CXX17_ALLOCATOR_VOID_DEPRECATION_WARNING
|
||||
<toolset>msvc:<define>_SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING
|
||||
<toolset>msvc:<define>_SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING
|
||||
<target-os>linux:<define>_XOPEN_SOURCE=600
|
||||
<target-os>linux:<define>_GNU_SOURCE=1
|
||||
<target-os>windows:<define>_WIN32_WINNT=0x0601
|
||||
;
|
||||
|
||||
# A static Asio library to speed up builds
|
||||
lib asio
|
||||
:
|
||||
tools/asio.cpp
|
||||
ssl
|
||||
crypto
|
||||
:
|
||||
<link>static
|
||||
$(requirements)
|
||||
<define>BOOST_ASIO_SEPARATE_COMPILATION
|
||||
:
|
||||
:
|
||||
$(requirements)
|
||||
<define>BOOST_ASIO_SEPARATE_COMPILATION
|
||||
;
|
||||
|
||||
alias mysql : asio ;
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 11 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 27 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 18 KiB |
|
@ -89,7 +89,7 @@
|
|||
[include 03_overview.qbk]
|
||||
[include 04_queries.qbk]
|
||||
[include 05_prepared_statements.qbk]
|
||||
[include 06_resultsets.qbk]
|
||||
[include 06_multi_function.qbk]
|
||||
[include 07_fields.qbk]
|
||||
[include 08_async.qbk]
|
||||
[include 09_ssl.qbk]
|
||||
|
|
|
@ -13,7 +13,7 @@ Welcome to Boost.Mysql's tutorial. We will go through the simplest
|
|||
possible piece of code using Boost.Mysql: a program that connects
|
||||
to the MySQL server and issues the query `SELECT "Hello World!"`.
|
||||
|
||||
This tutorial assumes you have a running MySQL server listening
|
||||
To run this tutorial, you need a running MySQL server listening
|
||||
in localhost on port 3306 (the default one). You should have
|
||||
the credentials of a valid MySQL user (username and password).
|
||||
No further setup is needed.
|
||||
|
@ -46,7 +46,8 @@ which accepts two parameters:
|
|||
|
||||
* The first one specifies the network address of the MySQL server.
|
||||
As we are using TCP, this is a [asioreflink ip__tcp/endpoint ip::tcp::endpoint],
|
||||
identifying the host and port to connect to.
|
||||
identifying the host and port to connect to. We will use a `boost::asio::ip::tcp::resolver`
|
||||
to convert the string hostname into a `boost::asio::ip::tcp::endpoint`.
|
||||
* The second one is a collection of MySQL handshake parameters, including
|
||||
username and password. This parameter must be an instance of
|
||||
[reflink handshake_params]. You may also set other options like
|
||||
|
@ -65,33 +66,25 @@ which accepts two parameters:
|
|||
The next step is to issue the query to the server. We will use
|
||||
[reflink2 connection.query tcp_ssl_connection::query],
|
||||
which accepts a string containing a single SQL query and instructs
|
||||
the server to run it. It returns a [reflink tcp_ssl_resultset]
|
||||
object, which allows to read the query results:
|
||||
the server to run it. It returns a [reflink resultset]
|
||||
object, containing the rows returned by the query:
|
||||
|
||||
[tutorial_query]
|
||||
|
||||
[heading Reading the results]
|
||||
[heading Obtaining the results]
|
||||
|
||||
A [reflink resultset] is an object that represents the result of a query.
|
||||
Resultsets are not containers, but I/O objects:
|
||||
they do not contain by themselves the entire result of the query, but allow
|
||||
the user to read it using several methods. We will use
|
||||
[reflink2 resultset.read_all tcp_ssl_resultset::read_all], which
|
||||
reads all the rows in the resultset and places them in a [reflink rows]
|
||||
object:
|
||||
A [reflink resultset] is an object that holds the result of a query in memory.
|
||||
To obtain the value we selected, we can write:
|
||||
|
||||
[tutorial_read]
|
||||
[tutorial_resultset]
|
||||
|
||||
A [reflink rows] object is a matrix-like container of MySQL fields.
|
||||
Each field is represented as a [reflink field_view], a variant-like class that
|
||||
can hold any type allowed in MySQL.
|
||||
|
||||
[tutorial_fields]
|
||||
|
||||
The operation `all_rows.at(0)` returns the first row from the `all_rows`
|
||||
container. From that row, we select the first field using `.at(0)` again.
|
||||
We finally stream the returned `field_view`
|
||||
to `std::cout`, which prints the expected phrase to the console.
|
||||
Let's break this into steps:
|
||||
* [refmem resultset rows] returns all the rows that this resultset contains.
|
||||
It returns a [reflink rows_view], which is a matrix-like structure.
|
||||
* `result.rows().at(0)` returns the first row in the resultset, represented as a [reflink row_view].
|
||||
* `result.rows().at(0).at(0)` returns the first field in the first row. This is a
|
||||
[reflink field_view], a variant-like class that can hold any type allowed in MySQL.
|
||||
* The obtained `field_view` is streamed to `std::cout`.
|
||||
|
||||
[heading Closing the connection]
|
||||
|
||||
|
@ -104,7 +97,7 @@ we are closing the connection, and thus may fail.
|
|||
|
||||
[heading Final notes]
|
||||
|
||||
This concludes our __Self__ tutorial! You can now learn more about the
|
||||
This concludes our tutorial! You can now learn more about the
|
||||
core functionality of this library in the [link mysql.overview overview section].
|
||||
You can also look at more complex [link mysql.examples examples].
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
[section:overview Overview]
|
||||
[nochunk]
|
||||
|
||||
[import ../../example/tutorial.cpp]
|
||||
|
||||
This section briefly explains the library main classes and functions, and how to use them.
|
||||
The following diagram shows the interaction between the main classes in the library:
|
||||
|
||||
|
@ -17,23 +19,42 @@ The following diagram shows the interaction between the main classes in the libr
|
|||
|
||||
[reflink connection] is the library's "entry point". A connection is an I/O object, templated on
|
||||
a [reflink Stream] type. A `connection` contains an instance of that `Stream` type and additional state required
|
||||
by the protocol. To create a `connection`, pass to its constructor the arguments required to construct a `Stream`.
|
||||
by the protocol. `connection`'s constructor takes the same arguments as the underlying `Stream` constructor.
|
||||
|
||||
The library defines some typedefs to make things less verbose. The most common one is [reflink tcp_ssl_connection].
|
||||
In this case, `Stream` is `boost::asio::ssl::stream<boost::asio::ip::tcp::socket>`,
|
||||
which can be constructed from a `io_context::executor_type` and a `ssl::context`:
|
||||
|
||||
```
|
||||
boost::asio::io_context ctx;
|
||||
boost::asio::ssl::context ssl_ctx(boost::asio::ssl::context::tls_client);
|
||||
boost::mysql::tcp_ssl_connection conn(ctx.get_executor(), ssl_ctx);
|
||||
```
|
||||
[tutorial_connection]
|
||||
|
||||
Typedefs for other transports are also available. See [link mysql.other_streams this section] for more info.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section Connection establishment]
|
||||
|
||||
The MySQL client/server protocol is session-oriented. Before anything else, you must perform session
|
||||
establishment, usually by calling [refmem connection connect]. We've already gone through this in the
|
||||
[link mysql.tutorial tutorial], so we won't repeat it here.
|
||||
establishment, usually by calling [refmem connection connect]. This function performs two actions:
|
||||
|
||||
* It establishes the "physical" connection, by calling `connect()` on the underlying `Stream`
|
||||
object. For a [reflink tcp_ssl_connection], this establishes the TCP connection.
|
||||
* It performs the handshake with the MySQL server. This is part of the MySQL client/server
|
||||
protocol. It performs authentication, sets session parameters like the default database
|
||||
to use, and performs the TLS handshake, if required.
|
||||
|
||||
[refmem connection connect] takes two parameters, one for each of the above actions:
|
||||
|
||||
* The physical endpoint where the server is listening. For TCP streams, this is a
|
||||
`boost::asio::ip::tcp::endpoint`. For UNIX sockets, it's a `boost::asio::local::stream_protocol::endpoint`.
|
||||
For TCP, we can resolve a string hostname and port to an endpoint using a `resolver` object.
|
||||
* [reflink handshake_params] to use for the handshake operation. This parameter doesn't depend on the `Stream`
|
||||
type. See [link mysql.connparams this section] for more info.
|
||||
|
||||
[tutorial_connect]
|
||||
|
||||
Note that [refmem connection connect] can only be used with socket-like streams. If your stream
|
||||
is not a socket, you must use the lower-level [refmem connection handshake] function. Please
|
||||
read [link mysql.other_streams.non_sockets this section] for more info.
|
||||
|
||||
[endsect]
|
||||
|
||||
|
@ -61,7 +82,7 @@ The two main ways to use a connection are text queries and prepared statements:
|
|||
]
|
||||
[
|
||||
```
|
||||
tcp_ssl_resultset result;
|
||||
resultset result;
|
||||
conn.query("START TRANSACTION", result);
|
||||
```
|
||||
]
|
||||
|
@ -75,33 +96,48 @@ The two main ways to use a connection are text queries and prepared statements:
|
|||
]
|
||||
[
|
||||
```
|
||||
tcp_ssl_connection conn;
|
||||
conn.connect(/* server host, password... */);
|
||||
|
||||
tcp_ssl_statement stmt;
|
||||
conn.prepare_statement("SELECT first_name FROM employees WHERE company_id = ? AND salary > ?");
|
||||
conn.prepare_statement("SELECT first_name FROM employees WHERE company_id = ? AND salary > ?", stmt);
|
||||
|
||||
tcp_ssl_resultset result;
|
||||
resultset result;
|
||||
stmt.execute(std::make_tuple("HGS", 30000), result);
|
||||
```
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
[reflink tcp_ssl_resultset] is a helper typedef for [reflink resultset]. Resultset objects
|
||||
represent the result of a query or statement execution. They hold auxiliar information
|
||||
about the operation, but don't contain the actual data generated by the operation.
|
||||
The next sections show how to obtain it.
|
||||
When you execute a text query or a prepared statement, you get a `resultset` object, which will be the subject
|
||||
of the next section. We will delve deeper into prepared statements [link mysql.overview.statements later].
|
||||
|
||||
[reflink tcp_ssl_statement] is a helper typedef for [reflink statement], which represent
|
||||
server-side prepared statements, as we've seen.
|
||||
[endsect]
|
||||
|
||||
[section Resultsets]
|
||||
|
||||
In MySQL, a ['resultset] referes to the results generated by a SQL query. The [reflink resultset] class
|
||||
is an in-memory reprentation of a MySQL resultset. The following diagram shows an approximate representation
|
||||
of what a resultset looks like:
|
||||
|
||||
[$mysql/images/resultset.svg [align center] [scale 125]]
|
||||
|
||||
We can see that a resultset is composed of three pieces of information:
|
||||
|
||||
* The actual rows generated by the SQL query: [refmem resultset rows]. We'll expand on this later.
|
||||
* Metadata about the columns retrieved by the query: [refmem resultset meta].
|
||||
* Additional information about the query execution, like the number of affected rows ([refmem resultset affected_rows])
|
||||
or the number of warnings generated by the query ([refmem resultset warning_count]).
|
||||
|
||||
You can obtain a `resultset` by executing a text query ([refmem connection query]) or a prepared statement
|
||||
([refmem statement execute]).
|
||||
|
||||
All SQL statements generate resultsets. Statements that generate no rows, like `INSERT`s, generate empty resultsets
|
||||
(i.e. `result.rows().empty() == true`). The interface to execute `SELECT`s and `INSERT`s is the same.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section Rows and fields]
|
||||
|
||||
Values retrieved from SQL queries are represented as variant-like objects called fields.
|
||||
Fields are organized in rows. This library defines the following types to represent fields and rows:
|
||||
We saw that [refmem resultset rows] returns a matrix-like data structure containing the rows
|
||||
retrieved by SQL query. This library defines six data structures to represent MySQL data:
|
||||
|
||||
[variablelist
|
||||
[
|
||||
|
@ -130,149 +166,50 @@ Fields are organized in rows. This library defines the following types to repres
|
|||
]
|
||||
]
|
||||
|
||||
[endsect]
|
||||
[refmem resultset rows] returns a [reflink rows_view] object. The memory for the rows is owned by the
|
||||
`resultset` object. Indexing the returned view also returns view objects:
|
||||
|
||||
[section:read Reading rows]
|
||||
```
|
||||
// Populate a resultset object
|
||||
resultset result;
|
||||
conn.query("SELECT 'Hello world'", result);
|
||||
|
||||
There are three methods to read rows from a `resultset`:
|
||||
// resultset::rows() returns a rows_view. The underlying memory is owned by the resultset
|
||||
rows_view all_rows = result.rows();
|
||||
|
||||
[variablelist
|
||||
[
|
||||
[[refmem resultset read_one]]
|
||||
[Reads rows one by one.]
|
||||
]
|
||||
[
|
||||
[[refmem resultset read_all]]
|
||||
[Reads all rows at once.]
|
||||
]
|
||||
[
|
||||
[[refmem resultset read_some]]
|
||||
[Reads rows in batches of unspecified size.]
|
||||
]
|
||||
]
|
||||
// Indexing a rows_view yields a row_view. The underlying memory is owned by the resultset
|
||||
row_view first_row = all_rows.at(0);
|
||||
|
||||
For each method, there is an "owning" version, which populates [reflink row] or [reflink rows]
|
||||
objects by lvalue reference; and a "non-owning" version, which returns [reflink row_view]
|
||||
or [reflink rows_view] objects, pointing to the `connection` internal buffers.
|
||||
These view objects are valid until you perform the next operation involving network
|
||||
transfers on the connection.
|
||||
// Indexing a row_view yields a field_view. The underlying memory is owned by the resultset
|
||||
field_view first_field = first_row.at(0); // Contains the string "Hello world"
|
||||
```
|
||||
|
||||
The following table shows the most used methods. For a full reference, see
|
||||
[link mysql.resultsets.read this section].
|
||||
Views behave similarly to `std::string_view`. You must make sure that you don't use a view after the
|
||||
storage its points to has gone out of scope. In this case, you must not use any of the views after the
|
||||
`resultset` object has gone out of scope.
|
||||
|
||||
[table
|
||||
[
|
||||
[Function]
|
||||
[Typical use]
|
||||
[Use when...]
|
||||
]
|
||||
[
|
||||
[
|
||||
[refmem resultset read_one], owning
|
||||
]
|
||||
[
|
||||
```
|
||||
row r;
|
||||
while (result.read_one(r))
|
||||
{
|
||||
// Process r as required
|
||||
std::cout << r << std::endl;
|
||||
}
|
||||
```
|
||||
]
|
||||
[
|
||||
* [*By default], for resultsets of arbitrary size.
|
||||
* When processing one row at a time is preferred.
|
||||
* A good alternative to [refmem resultset read_all], when all rows may not fit in memory.
|
||||
]
|
||||
]
|
||||
[
|
||||
[
|
||||
[refmem resultset read_all], owning
|
||||
]
|
||||
[
|
||||
```
|
||||
rows all_rows;
|
||||
result.read_all(all_rows);
|
||||
for (row_view r: all_rows)
|
||||
{
|
||||
// Process r as required
|
||||
std::cout << r << std::endl;
|
||||
}
|
||||
```
|
||||
]
|
||||
[
|
||||
* [*By default], for small resultsets.
|
||||
* When having all rows at the same time in memory is advantageous.
|
||||
]
|
||||
]
|
||||
[
|
||||
[
|
||||
[refmem resultset read_some], non-owning
|
||||
]
|
||||
[
|
||||
```
|
||||
while (!result.complete())
|
||||
{
|
||||
rows_view row_batch = result.read_some(use_views);
|
||||
As it happens with `std::string_view`, you can take ownership of a view using its owning counterpart:
|
||||
|
||||
for (row_view r: row_batch)
|
||||
{
|
||||
// Process r as required
|
||||
std::cout << r << std::endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
]
|
||||
[
|
||||
* [*By default], when efficiency is key.
|
||||
* There will be no server interaction that may invalidate the view while you process it.
|
||||
]
|
||||
]
|
||||
]
|
||||
```
|
||||
// You may use all_rows_owning after result has gone out of scope
|
||||
rows all_rows_owning {all_rows};
|
||||
|
||||
In the above snippets, we use [refmem resultset complete] to check whether there are more rows or not.
|
||||
We say a `resultset` is complete when there are no more rows to read. The server indicates this by sending
|
||||
us a special packet ("EOF packet" in MySQL slang). After a `resultset` is `complete`, you can access some
|
||||
additional information about the query execution, like [refmem resultset last_insert_id]
|
||||
and [refmem resultset affected_rows].
|
||||
// You may use first_row_owning after result has gone out of scope
|
||||
row first_row_owning {first_row};
|
||||
|
||||
[warning
|
||||
Once you call `connection::query` or `statement::execute`, the server immediately sends all rows to the client.
|
||||
[*You must read all rows], until the resultset is `complete`, before engaging in further operations.
|
||||
Otherwise, the results are undefined.
|
||||
]
|
||||
// You may use first_field_owning after result has gone out of scope
|
||||
field first_field_owning {first_field};
|
||||
```
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:rows_fields Using rows and fields]
|
||||
[section:fields Using fields]
|
||||
|
||||
All row types support random access iterators, as well as `at()` and `operator[]` with the usual semantics.
|
||||
[reflink field] and [reflink field_view] are specialized variant-like types that can hold any type
|
||||
you may find in a MySQL table. Once you obtain a field, you can access its contents using the following functions:
|
||||
|
||||
When iterating or indexing a [reflink row] object, you obtain a [reflink field_view], which is an object
|
||||
with reference semantics, pointing into `row`'s storage. You can "persist" a `field_view` by constructing a
|
||||
[reflink field] from it.
|
||||
|
||||
```
|
||||
tcp_ssl_resultset result;
|
||||
row r;
|
||||
|
||||
conn.query("SELECT \"abc\", 42", result);
|
||||
result.read_one(r);
|
||||
field_view ref = r[0]; // ref points to the string "abc", references r
|
||||
field f (ref); // f contains the string "abc"
|
||||
```
|
||||
|
||||
Indexing or iterating a `row_view` also yields `field_view`s. Similarly, indexing
|
||||
or iterating [reflink rows] or [reflink rows_view] objects yields a `row_view`.
|
||||
|
||||
[h4:fields Using fields]
|
||||
|
||||
Both `field` and `field_view` are specialized variant-like types that can hold any type
|
||||
you may find in a MySQL table. The following accessor operations are supported:
|
||||
|
||||
* You can query a field's type by using [reflink2 field_view.kind kind],
|
||||
which returns a [reflink field_kind] enum. You can query whether a field.
|
||||
* You can query a field's type by using [refmemunq field_view kind],
|
||||
which returns a [reflink field_kind] enum.
|
||||
* You can query whether a field contains a certain type with `field::is_xxx`.
|
||||
* You can get the underlying value with `field::as_xxx` and `field::get_xxx`.
|
||||
The `as_xxx` functions are checked (they will throw an exception if the
|
||||
|
@ -280,34 +217,66 @@ you may find in a MySQL table. The following accessor operations are supported:
|
|||
in undefined behavior on type mismatch).
|
||||
* You can stream and compare fields for equality.
|
||||
|
||||
Some examples:
|
||||
For example:
|
||||
|
||||
```
|
||||
tcp_ssl_resultset result;
|
||||
row r;
|
||||
|
||||
resultset result;
|
||||
conn.query("SELECT \"abc\", 42", result);
|
||||
result.read_one(r);
|
||||
|
||||
field_view f = r[0]; // ref points to the string "abc", references r
|
||||
// Using the is_xxx and get_xxx accessors
|
||||
field_view f = result.rows().at(0).at(0); // f points to the string "abc"
|
||||
if (f.is_string())
|
||||
{
|
||||
// we know it's a string, unchecked access
|
||||
boost::string_view s = f.get_string();
|
||||
std::cout << s << std::endl;
|
||||
std::cout << s << std::endl; // Use the string as required
|
||||
}
|
||||
else
|
||||
{
|
||||
// Oops, something went wrong - schema msimatch?
|
||||
}
|
||||
|
||||
// Checked access. Throws if f doesn't contain an int
|
||||
f = r[1];
|
||||
std::int64_t value = f.as_int64();
|
||||
std::cout << value << std::endl;
|
||||
// Using the as_xxx accessor
|
||||
f = result.rows().at(0).at(1);
|
||||
std::int64_t value = f.as_int64(); // Checked access. Throws if f doesn't contain an int
|
||||
std::cout << value << std::endl; // Use the int as required
|
||||
```
|
||||
|
||||
The following table shows the mapping from MySQL types to C++ types:
|
||||
`NULL` values are represented as field objects having `kind() == field_kind::null`.
|
||||
You can check whether a value is `NULL` or not using [refmemunq field_view is_null].
|
||||
In the following snippet, the `salary` column was defined as a nullable `double`.
|
||||
This is how `NULL`s are typically handled:
|
||||
|
||||
```
|
||||
resultset result;
|
||||
conn.query(R"%(
|
||||
SELECT 0 AS product_id, 'potatoes' as description UNION
|
||||
SELECT 1, NULL UNION
|
||||
SELECT 2, 'carrots'
|
||||
)%", result);
|
||||
|
||||
for (row_view r : result.rows())
|
||||
{
|
||||
field_view description_fv = r.at(1);
|
||||
if (description_fv.is_null())
|
||||
{
|
||||
// Handle the NULL value
|
||||
// Note: description_fv.is_string() will return false here; NULL is represented as a separate type
|
||||
std::cout << "No description for product_id " << r.at(0) << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Handle the non-NULL case. Get the underlying value and use it as you want
|
||||
// If there is any schema mismatch (and description was not defined as VARCHAR), this will throw
|
||||
boost::string_view description = description_fv.as_string();
|
||||
|
||||
// Use description as required
|
||||
std::cout << "product_id " << r.at(0) << ": " << description << std::endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Every MySQL type is mapped to a single C++ type. The following table shows these mappings:
|
||||
|
||||
[table
|
||||
[
|
||||
|
@ -411,46 +380,119 @@ The following table shows the mapping from MySQL types to C++ types:
|
|||
[link mysql.fields This section] contains more information about how the library maps
|
||||
MySQL types to C++ types.
|
||||
|
||||
[h4 Handling NULL values]
|
||||
[endsect]
|
||||
|
||||
`NULL` values are represented as `field`/`field_view` objects having `kind() == field_kind::null`.
|
||||
You can check whether a value is `NULL` or not using [refmemunq field_view is_null].
|
||||
In the following snippet, the `salary` column was defined as a nullable `double`.
|
||||
This is how `NULL`s are typically handled:
|
||||
[section:statements Using prepared statements]
|
||||
|
||||
Until now, we've used simple text queries that did not contain any user-provided input.
|
||||
In real world, most queries will contain some piece of user-provided input.
|
||||
|
||||
One approach could be to use string concatenation to construct a SQL query from user input,
|
||||
and then execute it using `query()`. Avoid this approach as much as possible, as it can lead
|
||||
to [*SQL injection vulnerabilities]. Instead, [*use prepared statements].
|
||||
|
||||
Prepared statements are server-side objects that represent a parameterized query. A statement is
|
||||
represented in this library using the [reflink statement] template class. `statement` is a proxy I/O
|
||||
object, as it references the `connection`'s stream and uses it for communication with the server.
|
||||
Thus, you must keep the `connection` alive while you're using the statements it created.
|
||||
|
||||
Let's say you've got an inventory table, and you're writing a command-line program to get products
|
||||
by ID. You've got the following table definition:
|
||||
|
||||
```
|
||||
tcp_ssl_resultset result;
|
||||
conn.query("SELECT last_name, salary FROM employees", result);
|
||||
resultset result;
|
||||
conn.query(R"%(
|
||||
CREATE TEMPORARY TABLE products (
|
||||
id VARCHAR(50) PRIMARY KEY,
|
||||
description VARCHAR(256)
|
||||
)
|
||||
)", result);
|
||||
conn.query("INSERT INTO products VALUES ('PTT', 'Potatoes'), ('CAR', 'Carrots')", result);
|
||||
```
|
||||
|
||||
row r;
|
||||
while(result.read_one(r))
|
||||
You can prepare a statement to retrieve products by ID using:
|
||||
|
||||
```
|
||||
tcp_ssl_statement stmt;
|
||||
conn.prepare_statement("SELECT description FROM products WHERE id = ?", stmt);
|
||||
```
|
||||
|
||||
[reflink tcp_ssl_statement] is a typedef for the `statement` class to make code less verbose.
|
||||
|
||||
You can execute the statement using [refmem statement execute]:
|
||||
|
||||
```
|
||||
// Obtain the product_id from the user. product_id is untrusted input
|
||||
const char* product_id = argv[2];
|
||||
|
||||
stmt.execute(std::make_tuple(product_id), result);
|
||||
// Use result as required
|
||||
```
|
||||
|
||||
You must pass as many actual parameters as `?` placeholders the statement has, as a `std::tuple`.
|
||||
|
||||
To learn more about prepared statements, please refer to [link mysql.prepared_statements this section].
|
||||
|
||||
[endsect]
|
||||
|
||||
[section Multi-function operations]
|
||||
|
||||
Until now, we've been using [refmem connection query] and [refmem statement execute], which send
|
||||
an execution request to the server, read the response and all generated data into an in-memory `resultset` object.
|
||||
|
||||
Some use cases may not fit in this simple pattern. For example:
|
||||
|
||||
* When reading a very big resultset, it may not be efficient (or even possible) to completely
|
||||
load it in memory. Reading rows in batches may be more adequate.
|
||||
* If rows contain very long `TEXT` or `BLOB` fields, it may not be adequate to copy these values
|
||||
from the network buffer into a `resultset`. A view-based approach may be better.
|
||||
|
||||
For these cases, we can break the `query()` or `execute()` operation into several steps,
|
||||
in a ['multi-function operation] (the term is coined by this library). This example reads an entire
|
||||
table row by row, which can be the case in an ETL process:
|
||||
|
||||
```
|
||||
// Create the table and some sample data
|
||||
// In a real system, body may be megabaytes long.
|
||||
resultset result;
|
||||
conn.query(R"%(
|
||||
CREATE TEMPORARY TABLE posts (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
title VARCHAR (256),
|
||||
body TEXT
|
||||
)
|
||||
)%", result);
|
||||
conn.query(R"%(
|
||||
INSERT INTO posts (title, body) VALUES
|
||||
('Post 1', 'A very long post body'),
|
||||
('Post 2', 'An even longer post body')
|
||||
)%", result);
|
||||
|
||||
// execution_state stores state about our operation, and must be passed to all functions
|
||||
execution_state st;
|
||||
|
||||
// Writes the query request and reads the server response, but not the rows
|
||||
conn.start_query("SELECT title, body FROM posts", st);
|
||||
|
||||
// post will be populated by read_one_row. When all rows have been read,
|
||||
// read_one_row returns false
|
||||
row post:
|
||||
while (conn.read_one_row(st, post))
|
||||
{
|
||||
boost::string_view last_name = r.at(0).as_string();
|
||||
field_view salary_fv = r.at(1);
|
||||
if (salary_fv.is_null())
|
||||
{
|
||||
// Handle the NULL value
|
||||
// Note: salary_fv.is_double() will return false here; NULL is represented as a separate type
|
||||
std::cout << "No salary stored for employee " << last_name << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Handle the non-NULL case. Get the underlying value and use it as you want
|
||||
// If there is any schema mismatch (and salary was not defined as DOUBLE), this will throw
|
||||
double salary = salary_fv.as_double();
|
||||
|
||||
// Use salary as required
|
||||
std::cout << "Employee " << last_name << " earns " << salary << "dollars yearly" << std::endl;
|
||||
}
|
||||
boost::string_view title = post.at(0).as_string();
|
||||
boost::string_view body = post.at(0).as_string();
|
||||
// use title and body as required
|
||||
}
|
||||
```
|
||||
|
||||
[note
|
||||
Even though the column was defined as `DOUBLE`, for a `NULL` field, `salary_fv.is_double() == false` and
|
||||
`salary_fv.as_double()` will throw an exception. At all effects, `NULL` values are not doubles!
|
||||
[warning
|
||||
Once you start a multi-function operation with [refmem connection start_query] or [refmem statement start_execution],
|
||||
the server immediately sends all rows to the client. [*You must read all rows] before engaging in further operations.
|
||||
Otherwise, you will encounter packet mismatches, which can lead to bugs and vulnerabilities!
|
||||
]
|
||||
|
||||
|
||||
Multi-function operations are powerful but complex. Only use them when there is a strong reason to do so.
|
||||
Please refer to [link mysql.multi_function this section] for more information on these operations.
|
||||
|
||||
[endsect]
|
||||
|
||||
|
@ -458,6 +500,10 @@ This is how `NULL`s are typically handled:
|
|||
|
||||
The library offers asynchronous versions of each operation involving network transfers.
|
||||
Following Asio's convention, these are named `async_xxx` (e.g. [refmem connection async_query]).
|
||||
They follow Asio's async model, so they can be used with any `CompletionToken`, including callbacks
|
||||
and coroutines. [link mysql.async This section] provides more info on this topic.
|
||||
|
||||
[h4 Single read and write per connection]
|
||||
|
||||
As mentioned, `connection` holds an internal `Stream` that is used for network transfers.
|
||||
All network operations involve stream [*reads], stream [*writes], or [*both]. At any given point in time, for
|
||||
|
@ -471,6 +517,7 @@ Doing this is illegal and should be avoided:
|
|||
```
|
||||
// Coroutine body
|
||||
// DO NOT DO THIS!!!!
|
||||
resultset result1, result2;
|
||||
auto q1 = conn.async_query("SELECT 1", result1, use_awaitable);
|
||||
auto q2 = conn.async_query("SELECT 2", result2, use_awaitable);
|
||||
co_await (q1 && q2);
|
||||
|
@ -480,34 +527,27 @@ If you need to perform queries in parallel, open more connections to the server.
|
|||
|
||||
[h4 Proxy I/O objects]
|
||||
|
||||
`resultset` and `statement` are proxy I/O objects: they use the stream created by the
|
||||
As mentioned, `statement` is a proxy I/O object: it uses the stream created by the
|
||||
`connection` to perform the required I/O. This means that [*read and write operations invoked
|
||||
on resultsets and statements also count toward the `connection` maximum]. This is illegal, too:
|
||||
on statements also count toward the `connection` maximum]. This is illegal, too:
|
||||
|
||||
```
|
||||
// Coroutine body
|
||||
// DO NOT DO THIS!!!!
|
||||
tcp_ssl_resultset result1, result2;
|
||||
rows all_rows;
|
||||
co_await conn.async_query("SELECT 1", result1, use_awaitable);
|
||||
resultset result1, result2;
|
||||
tcp_ssl_statement stmt;
|
||||
conn.prepare_statement("SELECT ?", stmt);
|
||||
|
||||
// Invokes two read operations in parallel - DO NOT DO THIS!!!
|
||||
auto query_aw = conn.async_query("SELECT 2", result2, use_awaitable);
|
||||
auto read_all_aw = result1.async_read_all(all_rows, use_awaitable);
|
||||
co_await (query_aw && read_all_aw);
|
||||
// Invokes two read and write operations in parallel on the same stream
|
||||
// DO NOT DO THIS!!!
|
||||
auto aw1 = conn.async_query("SELECT 1", result1, use_awaitable);
|
||||
auto aw2 = stmt.async_execute(std::make_tuple(42), result2, use_awaitable);
|
||||
co_await (aw1 && aw2);
|
||||
```
|
||||
|
||||
If you're working in a multi-threading environment, please bear in mind that
|
||||
[*network operations invoked on `statement` and `resultset` also mutate the
|
||||
[*network operations invoked on `statement`s also mutate the
|
||||
`connection`] that created them. Use strands as required to avoid race conditions.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:encoding Character sets, collations and time zones]
|
||||
|
||||
Encoding: set it on handshake or with SET NAMES and everything you send/receive will be in this encoding.
|
||||
We don't do any handling. TODO.
|
||||
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
||||
|
|
|
@ -7,43 +7,34 @@
|
|||
|
||||
[section:queries Text queries]
|
||||
|
||||
Text queries are started by calling the [refmem connection query]
|
||||
and [refmem connection async_query] functions. They accept
|
||||
a SQL query string as parameter, which will be executed in the server.
|
||||
They return a [reflink resultset] with the query results.
|
||||
To run a text query, use any of the following functions:
|
||||
|
||||
* [refmem connection query] or [refmem connection async_query], which execute the query and
|
||||
read the generated results.
|
||||
* [refmem connection start_query] and [refmem connection async_start_query], which initiate a
|
||||
text query as a multi-function operation.
|
||||
|
||||
Almost any query that may be issued in the `mysql` command line
|
||||
can be executed using this method. This includes `SELECT`s,
|
||||
`UPDATE`s, `INSERT`s, `DELETE`s, `CREATE TABLE`s...
|
||||
In particular, you may start transactions issuing a `START TRANSACTION`,
|
||||
commit them using `COMMIT` and rolling them back using `ROLLBACK`.
|
||||
You can also access the prepared statement functionality by
|
||||
issuing `PREPARE` and `EXECUTE` commands. However, it is
|
||||
advised to use [link mysql.prepared_statements
|
||||
the dedicated prepared statement functionality]
|
||||
in __Self__ for this task.
|
||||
|
||||
[heading Multi-function operation]
|
||||
|
||||
When you call [refmem connection query], you start a "multi-function" operation.
|
||||
`query()` will send the query request to the server. The server then sends an initial response
|
||||
and all the generated rows. `query()` will just read the initial response, and not the rows.
|
||||
|
||||
After a successful query, [*you must read all the generated rows] by calling any of the `read_xxx`
|
||||
functions on `resultset`. If you start any other operation involving a read before doing this,
|
||||
you will get undefined results.
|
||||
|
||||
[include helpers/query_strings_encoding.qbk]
|
||||
|
||||
[heading Limitations]
|
||||
|
||||
* You can only execute a single query at a time. Passing in several queries separated by semicolons
|
||||
won't work.
|
||||
* At the moment, there is no equivalent to `mysql_real_escape_string_quote` (TODO: link) to compose valid
|
||||
text queries from user provided input in a safe way. Doing composition by hand can
|
||||
will produce an error.
|
||||
* At the moment, there is no equivalent to
|
||||
[@https://dev.mysql.com/doc/c-api/8.0/en/mysql-real-escape-string.html `mysql_real_escape_string`]
|
||||
to sanitize user provided input. Doing composition by hand can
|
||||
lead to SQL injection vulnerabilities. Please use [link mysql.prepared_statements prepared statements]
|
||||
instead, which perform composition server-side in a safe way.
|
||||
|
||||
[warning
|
||||
[*SQL injection warning]: if you compose queries by concatenating strings without sanitization,
|
||||
your code is vulnerable to SQL injection attacks. Use prepared statements when possible!
|
||||
]
|
||||
|
||||
[heading Use cases]
|
||||
|
||||
You should generally prefer prepared statements over text queries. Text queries can be useful for simple,
|
||||
|
@ -55,13 +46,4 @@ non-parametrized queries:
|
|||
|
||||
Avoid text queries involving user input.
|
||||
|
||||
[heading Examples]
|
||||
|
||||
* [link mysql.examples.query_sync Sync query].
|
||||
* [link mysql.examples.query_async_callbacks Async query, with callbacks].
|
||||
* [link mysql.examples.query_async_futures Async query, with futures].
|
||||
* [link mysql.examples.query_async_coroutines Async query, with Boost.Coroutine coroutines].
|
||||
* [link mysql.examples.query_async_coroutinescpp20 Async query, with C++20 coroutines].
|
||||
|
||||
|
||||
[endsect]
|
||||
[endsect]
|
||||
|
|
|
@ -7,116 +7,114 @@
|
|||
|
||||
[section:prepared_statements Prepared statements]
|
||||
|
||||
[import ../../example/prepared_statements.cpp]
|
||||
|
||||
This section covers using [mysqllink sql-prepared-statements.html
|
||||
server-side prepared statements]. The functionality is broadly
|
||||
similar to the `PREPARE`, `EXECUTE` and `DEALLOCATE`
|
||||
SQL commands. However, note that the functions and classes
|
||||
here described are [*not based on SQL query composition],
|
||||
but make use of dedicated protocol functionality.
|
||||
This makes using this API preferable to using query composition
|
||||
in terms of security and efficiency.
|
||||
|
||||
The rest of this section contains a detailed explanation of
|
||||
prepared statement mechanics, together with some code
|
||||
snippets. You can find the full code listing
|
||||
[link mysql.examples.prepared_statements here].
|
||||
server-side prepared statements]. You should use them whenever a query
|
||||
contains parameters not known at compile-time.
|
||||
|
||||
[heading Preparing a statement]
|
||||
|
||||
To prepare a statement, call [refmem connection prepare_statement]
|
||||
or [refmem connection async_prepare_statement]. You must pass in
|
||||
a string containing the text of the SQL statement (similar
|
||||
to how [link mysql.queries text queries] work). These functions
|
||||
return a [reflink statement] object.
|
||||
or [refmem connection async_prepare_statement], passing your statement
|
||||
as a string. This yields a [reflink statement] object, which is templated
|
||||
on the `Stream` type:
|
||||
|
||||
In addition to regular SQL, you can also use
|
||||
question mark characters (`?`) to represent parameters
|
||||
```
|
||||
// Table setup
|
||||
resultset result;
|
||||
conn.query(R"%(
|
||||
CREATE TEMPORARY TABLE products (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
description VARCHAR(256),
|
||||
price INT NOT NULL,
|
||||
show_in_store TINYINT
|
||||
)
|
||||
)%", result);
|
||||
|
||||
// Prepare a statement to insert into this table
|
||||
tcp_ssl_statement stmt;
|
||||
conn.prepare_statement("INSERT INTO products (description, price, show_in_store) VALUES (?, ?, ?)", stmt);
|
||||
```
|
||||
|
||||
The question mark characters (`?`) represent parameters
|
||||
(as described [mysqllink prepare.html here]).
|
||||
When you execute the statement (next section), you
|
||||
provide values for each of the parameters you declared.
|
||||
You don't need to escape or sanitize these values anyhow,
|
||||
thus avoiding the possibility of SQL injection attacks.
|
||||
|
||||
The following prepares a statement with one
|
||||
parameter (`conn` is a [reflink tcp_ssl_connection]):
|
||||
|
||||
[prepared_statements_prepare]
|
||||
|
||||
[include helpers/query_strings_encoding.qbk]
|
||||
provide values for each of the parameters you declared, and the server
|
||||
will use these values to run the statement.
|
||||
|
||||
[heading Executing a statement]
|
||||
|
||||
To execute a statement, use [refmem statement execute] or [refmem statement async_execute].
|
||||
Executing a statement yields a [reflink resultset].
|
||||
To execute a statement, use any of the following functions:
|
||||
|
||||
When executing a statement, you must pass in [*exactly as many parameters
|
||||
as the statement has]. Failing to do so will result in an error.
|
||||
Parameters are passed as a `std::tuple`, and in the same order as they
|
||||
appear in the statement text. You can pass in built-in integers,
|
||||
`float`, `double`, [reflink date], [reflink datetime], [reflink time],
|
||||
[reflink field_view] and [reflink field] objects as parameters,
|
||||
with the expected results. You can use `nullptr` objects to represent `NULL`
|
||||
parameters. If your statement doesn't have any parameters, use the
|
||||
[reflink no_statement_params] variable.
|
||||
* [refmem statement execute] or [refmem statement async_execute], which execute the statement and
|
||||
read the generated results.
|
||||
* [refmem statement start_execution] and [refmem statement async_start_execution], which initiate a
|
||||
statement execution as a multi-function operation.
|
||||
|
||||
The following executes the statement we prepared in the previous
|
||||
section, binding the `first_name` parameter to `"Efficient"`:
|
||||
|
||||
[prepared_statements_execute]
|
||||
|
||||
Note that MySQL is pretty flexible and will perform casting to try to match
|
||||
the supplied value to the required type, depending on the context.
|
||||
|
||||
In particular, for integer types, conversions to narrower integer types
|
||||
will take place as long as the supplied value is in range. For example:
|
||||
For example:
|
||||
|
||||
```
|
||||
// Table created using: CREATE TABLE my_table (field_int TINYINT)
|
||||
tcp_ssl_statement stmt;
|
||||
conn.prepare_statement("INSERT INTO my_table (field_int) VALUES (?)", stmt);
|
||||
|
||||
tcp_ssl_resultset result;
|
||||
std::int64_t value = 42; // OK, in range
|
||||
stmt.execute(std::make_tuple(value), result);
|
||||
|
||||
value = 0xffff; // Oops, out of range
|
||||
stmt.execute(std::make_tuple(value), result); // will throw
|
||||
// description, price and show_in_store are not trusted, since they may
|
||||
// have been read from a file or an HTTP endpoint
|
||||
void insert_product(
|
||||
tcp_ssl_statement& stmt,
|
||||
boost::string_view description,
|
||||
int price,
|
||||
bool show_in_store
|
||||
)
|
||||
{
|
||||
resultset result;
|
||||
stmt.execute(std::make_tuple(description, price, int(show_in_store)), result);
|
||||
}
|
||||
```
|
||||
|
||||
To insert a `NULL` value, use the following:
|
||||
Some observations:
|
||||
|
||||
* You must pass in [*exactly as many parameters
|
||||
as the statement has]. Failing to do so will result in an error.
|
||||
* You don't need to sanitize the parameters anyhow. The server takes care of it.
|
||||
* Actual parameters are matched to `?` placeholders by order.
|
||||
* Parameters are passed as a `std::tuple`. You can pass in built-in integers,
|
||||
`float`, `double`, [reflink date], [reflink datetime], [reflink time],
|
||||
[reflink field_view] and [reflink field] objects as parameters.
|
||||
* `show_in_store` is passed as an `int` to `execute()`, but is defined as a `TINYINT`
|
||||
(1 byte integer) in the table. As long as the passed integer is in range, MySQL
|
||||
will perform the required conversions. Otherwise, `execute()` will fail with an error
|
||||
(no undefined behavior is invoked).
|
||||
|
||||
You can also pass [reflink field_view]s and [reflink field]s as parameters. This is handy
|
||||
to insert `NULL` values:
|
||||
|
||||
```
|
||||
stmt.execute(std::make_tuple(nullptr), result);
|
||||
// description, price and show_in_store are not trusted, since they may
|
||||
// have been read from a file or an HTTP endpoint
|
||||
void insert_product(
|
||||
tcp_ssl_statement& stmt,
|
||||
std::optional<boost::string_view> description,
|
||||
int price,
|
||||
bool show_in_store
|
||||
)
|
||||
{
|
||||
// if description has a value, description_param will have kind() == field_kind::string
|
||||
// and will point to it. Otherwise, description_param.kind() == field_kind::null
|
||||
auto description_param = description ? field_view(*description) : field_view();
|
||||
|
||||
// Execute the insert
|
||||
resultset result;
|
||||
stmt.execute(std::make_tuple(description_param, price, int(show_in_store)), result);
|
||||
}
|
||||
```
|
||||
|
||||
For a full reference on the types you can pass as parameters when
|
||||
executing a statement, see [link mysql.fields.cpp_to_mysql this section].
|
||||
|
||||
[heading Multi-function operation]
|
||||
|
||||
When you call [refmem statement execute], you start a "multi-function" operation.
|
||||
`execute()` will send the execute request to the server. The server then sends an initial response
|
||||
and all the generated rows. `execute()` will just read the initial response, and not the rows.
|
||||
|
||||
After a successful `execute()`, [*you must read all the generated rows] by calling any of the `read_xxx`
|
||||
functions on `resultset`. If you start any other operation involving a read before doing this,
|
||||
you will get undefined results.
|
||||
|
||||
[heading Closing a statement]
|
||||
|
||||
Prepared statements are created server-side, and
|
||||
thus consume server resources. If you don't need a
|
||||
[reflink statement] anymore, you can call
|
||||
[refmem statement close] or
|
||||
[refmem statement async_close] to instruct
|
||||
the server to deallocate it.
|
||||
Prepared statements are created server-side, and thus consume server resources. If you don't need a
|
||||
[reflink statement] anymore, you can call [refmem statement close] or
|
||||
[refmem statement async_close] to instruct the server to deallocate it.
|
||||
|
||||
Prepared statements are managed by the server on a
|
||||
per-connection basis. Once you close your connection
|
||||
with the server, all prepared statements you have
|
||||
created using this connection will be automatically
|
||||
Prepared statements are managed by the server on a per-connection basis. Once you close your connection
|
||||
with the server, all prepared statements you have created using this connection will be automatically
|
||||
deallocated.
|
||||
|
||||
If you are creating your prepared statements at the beginning
|
||||
|
@ -132,4 +130,4 @@ does not perform any server-side deallocation of the statement.
|
|||
This is because closing a statement involves a network
|
||||
operation that may block your code or fail.
|
||||
|
||||
[endsect]
|
||||
[endsect]
|
||||
|
|
|
@ -0,0 +1,222 @@
|
|||
[/
|
||||
Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
|
||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
]
|
||||
|
||||
[section:multi_function Multi-function operations]
|
||||
[nochunk]
|
||||
|
||||
Multi-function operations allow running operations as a set of separate
|
||||
steps, which gives you better control over execution. They work by splitting
|
||||
some of the reads and writes into several function calls.
|
||||
You can use multi-function operations to execute text queries and prepared statements.
|
||||
|
||||
[heading Protocol primer]
|
||||
|
||||
To make a good use of multi-function operations, you should have a basic understanding
|
||||
of the underlying protocol.
|
||||
|
||||
The protocol uses ['messages] to communicate. These are delimited by headers containing the message length.
|
||||
All operations are initiated by the client, by sending a single ['request message], to which
|
||||
the server responds with a set of ['response messages].
|
||||
|
||||
The diagram below shows the message exchange between client and server for text queries and statement
|
||||
executions. Each arrow represents a message.
|
||||
|
||||
[$mysql/images/protocol.svg [align center]]
|
||||
|
||||
|
||||
The message exchange is similar for text queries and prepared statements. The wire format varies, but the semantics
|
||||
are the same.
|
||||
|
||||
There are two separate cases:
|
||||
|
||||
* If your query retrieved at least one column (even if no rows were generated), we're in ['case 1]. The server sends:
|
||||
* An initial packet informing that the query executed correctly, and that we're in ['case 1].
|
||||
* Some matadata packets describing the columns that the query retrieved. These become available
|
||||
under [refmem resultset meta] and [refmem execution_state meta], and are necessary to parse the rows.
|
||||
* The actual rows.
|
||||
* An OK packet, which marks the end of the resultset and contains information like `last_insert_id` and
|
||||
`affected_rows`.
|
||||
* If your query didn't retrieve any column, we're in ['case 2]. The server will just send an OK packet,
|
||||
with the same information as in ['case 1].
|
||||
|
||||
[refmem connection query] and [refmem statement execute] handle the full message exchange. In contrast,
|
||||
[refmem connection start_query] and [refmem statement start_execution] will not read the rows, if any.
|
||||
|
||||
Some takeaways:
|
||||
|
||||
* The distinction between single-function and multi-function operations exists only
|
||||
in the client. The wire messages exchanged by both are the same.
|
||||
* There is no way to tell how many rows a resultset has upfront. You need to read row by row until
|
||||
you find the OK packet marking the end of the resultset.
|
||||
* When the server processes the request message, [*it sends all the response messages immediately].
|
||||
These responses will be waiting in the client to be read. If you don't read [*all] of them,
|
||||
subsequent operations will mistakenly read them as their response, causing packet mismatches.
|
||||
Be careful and don't let this happen!
|
||||
|
||||
[heading Starting a multi-function operation]
|
||||
|
||||
Given the following setup:
|
||||
|
||||
```
|
||||
// TODO: this is repeated from the overview section
|
||||
resultset result;
|
||||
conn.query(R"%(
|
||||
CREATE TEMPORARY TABLE posts (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
title VARCHAR (256),
|
||||
body TEXT
|
||||
)
|
||||
)%", result);
|
||||
conn.query(R"%(
|
||||
INSERT INTO posts (title, body) VALUES
|
||||
('Post 1', 'A very long post body'),
|
||||
('Post 2', 'An even longer post body')
|
||||
)%", result);
|
||||
|
||||
|
||||
// This is not repeated
|
||||
tcp_ssl_statement stmt;
|
||||
conn.prepare_statement("SELECT title, body FROM posts", stmt);
|
||||
```
|
||||
|
||||
You can start a multi-function operation using [refmem connection start_query] or [refmem statement start_execution]:
|
||||
|
||||
[table
|
||||
[
|
||||
[Text queries]
|
||||
[Prepared statements]
|
||||
]
|
||||
[
|
||||
[
|
||||
```
|
||||
execution_state st;
|
||||
conn.start_query("SELECT title, body FROM posts", st);
|
||||
```
|
||||
]
|
||||
[
|
||||
```
|
||||
execution_state st;
|
||||
stmt.start_execution(std::make_tuple(), st); // The statement has no params, so an empty tuple is passed
|
||||
```
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
[heading Reading rows]
|
||||
|
||||
Once the operation has been started, you can read rows in two ways:
|
||||
|
||||
* [refmem connection read_one_row] reads a single row per call.
|
||||
* [refmem connection read_some_rows] reads a batch of an unspecified size.
|
||||
|
||||
The following table shows examples on how to use each method (where `st` is the [reflink execution_state]
|
||||
you passed to the `start_query()` or `start_execution()` function):
|
||||
|
||||
[table
|
||||
[
|
||||
[Function]
|
||||
[Typical use]
|
||||
[Remarks]
|
||||
]
|
||||
[
|
||||
[
|
||||
[refmem connection read_one_row], non-owning
|
||||
]
|
||||
[
|
||||
```
|
||||
while (true)
|
||||
{
|
||||
// post will be valid until conn performs any other network operation
|
||||
// when the OK packet is read, post will be an empty view
|
||||
row_view post = conn.read_one_row(st);
|
||||
|
||||
// st.complete() returns true once the OK packet is received
|
||||
if (st.complete())
|
||||
break;
|
||||
|
||||
// Process post as required
|
||||
std::cout << post << std::endl;
|
||||
}
|
||||
```
|
||||
]
|
||||
[
|
||||
* When the final OK packet is found, `read_one_row` returns an empty view and `st.complete() == true`.
|
||||
* May be slower than `read_some_rows`, but it may consume less memory.
|
||||
* Calling `read_one_row` after reading the final OK packet returns an empty view.
|
||||
]
|
||||
]
|
||||
[
|
||||
[
|
||||
[refmem connection read_some_rows], non-owning
|
||||
]
|
||||
[
|
||||
```
|
||||
// st.complete() returns true once the OK packet is received
|
||||
while (!st.complete())
|
||||
{
|
||||
// row_batch will be valid until conn performs any other network operation
|
||||
rows_view row_batch = conn.read_some_rows(st);
|
||||
|
||||
for (row_view post : row_batch)
|
||||
{
|
||||
// Process post as required
|
||||
std::cout << post << std::endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
]
|
||||
[
|
||||
* The final `row_batch` may or may not be empty, depending on the number of rows and their size.
|
||||
* Usually the fastest alternative.
|
||||
* Calling `read_some_rows` after reading the final OK packet returns an empty batch.
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
Both functions return view objects pointing into the connection's internal buffers. These views are valid
|
||||
until the connection performs any other operation involving a network transfer.
|
||||
|
||||
[refmem execution_state complete] returns `true` after we've read the final OK packet for this resultset.
|
||||
We use it to exit our read loop.
|
||||
|
||||
You can take ownserhip of the views using the [reflink row] and [reflink rows] classes. Note however that these
|
||||
are immutable types (in that they don't have mutating functions other than assignment), designed to maximize memory reuse.
|
||||
If you need to mutate a row, you can use [refmem row_view as_vector] to obtain a `std::vector<field>`.
|
||||
|
||||
Note that there is no need to distinguish between ['case 1] and ['case 2] in code, as reading rows for
|
||||
a complete operation is well defined.
|
||||
|
||||
[heading Accessing metadata and OK packet data]
|
||||
|
||||
You can access metadata at any point, using [refmem execution_state meta]. This function returns a collection of [reflink metadata]
|
||||
objects. There is one object for each column retrieved by the SQL query, and in the same order as in the query. You can find a bunch
|
||||
of useful information in this object, like the column name, its type, whether it's a key or not, and so on.
|
||||
|
||||
You can access OK packet data using functions like [refmem execution_state last_insert_id]
|
||||
and [refmem execution_state affected_rows]. As this information is contained in the OK packet,
|
||||
[*these functions have `st.complete() == true` as precondition].
|
||||
|
||||
|
||||
[heading:read_some_rows More on read_some_rows]
|
||||
|
||||
To properly understand `read_some_rows`, we need to know that every [reflink connection]
|
||||
owns an internal *read buffer*, where packets sent by the server are stored.
|
||||
It is a single, flat buffer, and you can configure its initial size using
|
||||
[refmem handshake_params buffer_config] when establishing the connection.
|
||||
The read buffer may be grown under certain circumstances to accomodate large messages.
|
||||
|
||||
`read_some_rows` gets the maximum number of rows that fit in the read buffer (without growing it)
|
||||
performing a single `read_some_rows` operation on the stream (or using cached data).
|
||||
If there are rows to read, `read_some_rows` guarantees to read at least one. This means that,
|
||||
if doing what we described yields no rows (e.g. because of a large row that doesn't fit
|
||||
into the read buffer), `read_some_rows` will grow the buffer or perform more reads until at least
|
||||
one row has been read.
|
||||
|
||||
If you want to get the most of `read_some_rows`, customize the initial read buffer size
|
||||
to maximize the number of rows that each batch retrieves.
|
||||
|
||||
[endsect]
|
|
@ -1,331 +0,0 @@
|
|||
[/
|
||||
Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
|
||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
]
|
||||
|
||||
[section:resultsets Resultsets]
|
||||
[nochunk]
|
||||
|
||||
[reflink2 resultset Resultset objects] represent the result of a SQL query.
|
||||
You can obtain a `resultset` by executing a [link mysql.queries text query]
|
||||
or a [link mysql.prepared_statements prepared statement].
|
||||
|
||||
A `resultset` contains metadata and state about the query or statement being
|
||||
executed, but not the actual rows.
|
||||
|
||||
[section:read Reading rows]
|
||||
|
||||
Recall the following points from [link mysql.overview.read this section]:
|
||||
|
||||
* [reflink row] and [reflink row_view] represent a single row, as a collection of fields.
|
||||
The first one is owning, and the second one is not.
|
||||
* [reflink rows] and [reflink rows_view] represent several rows of the same size.
|
||||
* There are three methods to read rows:
|
||||
* [refmem resultset read_one] reads a single row.
|
||||
* [refmem resultset read_some] reads a batch of an unspecified size.
|
||||
* [refmem resultset read_all] reads all rows at once.
|
||||
* Each method provides two overloads:
|
||||
* An "owning" overload, which populates a [reflink row] or [reflink rows] by lvalue reference.
|
||||
* A "view" overload (taking a [reflink use_views_t] placeholder), which returns views into internal buffers.
|
||||
* You can use [refmem resultset complete] to check whether we've read all rows for this resultset or not.
|
||||
|
||||
This yields 6 different methods to retrieve rows. The following table shows them all:
|
||||
|
||||
|
||||
[table
|
||||
[
|
||||
[Function]
|
||||
[Typical use]
|
||||
[Use when...]
|
||||
]
|
||||
[
|
||||
[
|
||||
[refmem resultset read_one], owning
|
||||
]
|
||||
[
|
||||
```
|
||||
row r;
|
||||
while (result.read_one(r))
|
||||
{
|
||||
// Process r as required
|
||||
std::cout << r << std::endl;
|
||||
}
|
||||
```
|
||||
]
|
||||
[
|
||||
* [*By default], for resultsets of arbitrary size.
|
||||
* When processing one row at a time is preferred.
|
||||
* A good alternative to [refmem resultset read_all], when all rows may not fit in memory.
|
||||
]
|
||||
]
|
||||
[
|
||||
[
|
||||
[refmem resultset read_all], owning
|
||||
]
|
||||
[
|
||||
```
|
||||
rows all_rows;
|
||||
result.read_all(all_rows);
|
||||
for (row_view r: all_rows)
|
||||
{
|
||||
// Process r as required
|
||||
std::cout << r << std::endl;
|
||||
}
|
||||
```
|
||||
]
|
||||
[
|
||||
* [*By default], for small resultsets.
|
||||
* When having all rows at the same time in memory is advantageous.
|
||||
]
|
||||
]
|
||||
[
|
||||
[
|
||||
[refmem resultset read_some], owning
|
||||
]
|
||||
[
|
||||
```
|
||||
rows row_batch;
|
||||
while (!result.complete())
|
||||
{
|
||||
result.read_some(row_batch);
|
||||
|
||||
for (row_view r: row_batch)
|
||||
{
|
||||
// Process r as required
|
||||
std::cout << r << std::endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
]
|
||||
[
|
||||
* When efficiency is key.
|
||||
* You can't use the non-owning version of `read_some` because of view validity issues
|
||||
(more common in its async form).
|
||||
]
|
||||
]
|
||||
[
|
||||
[
|
||||
[refmem resultset read_one], non-owning
|
||||
]
|
||||
[
|
||||
```
|
||||
while (true)
|
||||
{
|
||||
row_view r = result.read_one(use_views);
|
||||
if (result.complete())
|
||||
break;
|
||||
|
||||
// Process r as required
|
||||
std::cout << r << std::endl;
|
||||
}
|
||||
```
|
||||
]
|
||||
[
|
||||
* When efficiency is important.
|
||||
* When processing one row at a time is preferred.
|
||||
* There will be no server interaction that may invalidate the view while you process it.
|
||||
]
|
||||
]
|
||||
[
|
||||
[
|
||||
[refmem resultset read_all], non-owning
|
||||
]
|
||||
[
|
||||
```
|
||||
rows_view all_rows = result.read_all(use_views);
|
||||
for (row_view r: all_rows)
|
||||
{
|
||||
// Process r as required
|
||||
std::cout << r << std::endl;
|
||||
}
|
||||
```
|
||||
]
|
||||
[
|
||||
* When efficiency is important.
|
||||
* The resultset is small, as it must fit in memory.
|
||||
* Having all rows at the same time in memory is advantageous.
|
||||
* There will be no server interaction that may invalidate the view while you process it.
|
||||
]
|
||||
]
|
||||
[
|
||||
[
|
||||
[refmem resultset read_some], non-owning
|
||||
]
|
||||
[
|
||||
```
|
||||
while (!result.complete())
|
||||
{
|
||||
rows_view row_batch = result.read_some(use_views);
|
||||
|
||||
for (row_view r: row_batch)
|
||||
{
|
||||
// Process r as required
|
||||
std::cout << r << std::endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
]
|
||||
[
|
||||
* [*By default], when efficiency is key.
|
||||
* There will be no server interaction that may invalidate the view while you process it.
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
Some additional guidelines to help you choose between methods:
|
||||
|
||||
* If you don't need maximum efficiency, and your resultset is small,
|
||||
use the [*owning] version of `read_all`. Note that this doesn't mean
|
||||
`read_all` is inefficient, as all functions have been optimized for maximum memory re-use.
|
||||
* If you don't need maximum efficiency, and your resultset is really big (e.g. all rows wouldn't fit in memory),
|
||||
use the [*owning] version of `read_one`.
|
||||
* If your resultset contains very, very long strings, use any of the [*non-owning] functions, which
|
||||
avoid some copying.
|
||||
* If you need maximum efficiency, use the [*non-owning] version of `read_some`.
|
||||
* If you need to keep your data valid between operations (e.g. you get a batch of rows,
|
||||
and send them through an HTTP link while you get the next batch of rows), use any of the [*owning] methods.
|
||||
* Measure before making efficiency-related decisions, and try to keep it simple!
|
||||
|
||||
[heading Lifetimes]
|
||||
|
||||
If you read with an "owning" function, you get a [reflink row] or [reflink rows] object.
|
||||
These classes have value semantics, and are guaranteed to be valid even after the resultset
|
||||
that read them is destroyed.
|
||||
|
||||
Any view type you obtain from a `row` or `rows` is valid until the owning object is destroyed
|
||||
or assigned to (similarly to how references to elements in a `vector` work):
|
||||
|
||||
```
|
||||
rows all_rows;
|
||||
|
||||
// Populate it
|
||||
tcp_ssl_resultset result;
|
||||
conn.query("SELECT 1", result);
|
||||
result.read_all(all_rows);
|
||||
|
||||
// rv references all_rows; valid until all_rows is destroyed
|
||||
row_view rv = all_rows.at(0);
|
||||
|
||||
// fv references all_rows, too; same ownership rules
|
||||
field_view fv = rv[0];
|
||||
|
||||
// Replace the original all_rows object.
|
||||
// Views referencing all_rows are invalidated
|
||||
conn.query("SELECT 1", result);
|
||||
result.read_all(all_rows);
|
||||
|
||||
// Do NOT use rv or fv here - dangling views
|
||||
```
|
||||
|
||||
Note that both `row` and `rows` [*are immutable types] (in that they don't have mutating functions other than assignment).
|
||||
They are designed to maximize memory re-use when reading rows. If you need to mutate a row, you can use [refmem row as_vector]
|
||||
or [refmem row_view as_vector] to obtain a `std::vector<field>`.
|
||||
|
||||
If you read with a "non-owning" function, the returned view points into the underlying
|
||||
`connection` internal read buffer. This means that [*any operation implying a stream read]
|
||||
on the `connection` or associated `statement` and `resultset` objects invalidates the view.
|
||||
For example:
|
||||
|
||||
```
|
||||
tcp_ssl_resultset result1, result2;
|
||||
|
||||
// Issue the first query
|
||||
conn.query("SELECT 1", result1);
|
||||
|
||||
// Get all the rows, as a view. all_rows points into
|
||||
// conn's internal buffers
|
||||
rows_view all_rows = result1.read_all(use_views);
|
||||
|
||||
// Issue the second query. A query implies a read operation
|
||||
// on the underlying stream, so this line
|
||||
// INVALIDATES all_rows!
|
||||
conn.query("SELECT 2", result2);
|
||||
|
||||
// Do NOT use all_rows here - dangling view!!
|
||||
```
|
||||
|
||||
[heading:read_some More on read_some]
|
||||
|
||||
To properly understand `read_one`, we need to know that every [reflink connection]
|
||||
owns an internal *read buffer*, where packets sent by the server are stored.
|
||||
It is a single, flat buffer, and you can configure its initial size using
|
||||
[refmem handshake_params buffer_config] when establishing the connection.
|
||||
The read buffer may be grown under certain circumstances to accomodate large messages.
|
||||
|
||||
`read_some` gets the maximum number of rows that fit in the read buffer (without growing it)
|
||||
performing a single `read_some` operation on the stream (or using cached data).
|
||||
If there are rows to read, `read_some` guarantees to read at least one. This means that,
|
||||
if doing what we described yields no rows (e.g. because of a large row that doesn't fit
|
||||
into the read buffer), `read_one` will grow the buffer or perform more reads until at least
|
||||
one row has been read.
|
||||
|
||||
If you want to get the most of `read_some`, customize the initial read buffer size
|
||||
to maximize the number of rows that each batch retrieves.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:complete Resultsets becoming complete]
|
||||
|
||||
When you have read every single row in a [reflink resultset],
|
||||
then we say the resultset is [*complete]. You can query for this
|
||||
fact calling [refmem resultset complete].
|
||||
|
||||
If a resultset comes from a SQL statement that generates rows
|
||||
(e.g. a `SELECT` statement that matches some rows), it completes
|
||||
the first time you try to read a row, but there are not any more available.
|
||||
For example, in a resultset with 4 rows, any of the following actions will
|
||||
complete the resultset:
|
||||
|
||||
* Calling [refmem resultset read_one] 5 times.
|
||||
* Calling [refmem resultset read_all].
|
||||
|
||||
If the SQL statement did not generate any rows, we say that the resultset
|
||||
is [*empty]. This happens for `UPDATE` or `INSERT` statements. Empty resultsets
|
||||
are complete from the beginning: you don't need to
|
||||
call [refmem resultset read_one] to make them complete.
|
||||
|
||||
After a [reflink resultset] is complete, some extra information about
|
||||
the query becomes available, like [refmem resultset warning_count]
|
||||
or [refmem resultset affected_rows]. MySQL sends this information
|
||||
as an extra packet only after sending every single resultset row,
|
||||
hence this mechanic.
|
||||
|
||||
Calling any row-reading function on a complete resultset is well defined
|
||||
and has the expected effects.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:multifunction Multi-function operations]
|
||||
|
||||
Resultsets are generated by multi-function operations like [refmem connection query]
|
||||
or [refmem statement execute]. These operations will send the execution request to the server.
|
||||
The server then sends an initial response and all the generated rows.
|
||||
`query()` and `execute()` will just read the initial response, and not the rows.
|
||||
|
||||
This means that the rows will be waiting in the client's read buffer, and must be read
|
||||
before engaging in further operations. Once you get a `resultset`,
|
||||
[*you must read all the generated rows], until [refmem resultset complete]
|
||||
returns `true`. If you start any other operation involving a read before doing this,
|
||||
you will get packet mismatches and undefined results.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:metadata Metadata]
|
||||
|
||||
Resultset objects hold metadata describing the columns they
|
||||
contain. You can access these data using [refmem resultset meta].
|
||||
This function returns a collection of [reflink metadata]
|
||||
objects. There is one object for each column retrieved by the SQL query,
|
||||
and in the same order as in the query. You can find a bunch
|
||||
of useful information in this object, like the column name,
|
||||
its type, whether it's a key or not, and so on.
|
||||
|
||||
Metadata is always available (i.e. you don't need [refmem resultset complete]
|
||||
to return `true` before accessing it). For empty resultsets, [refmem resultset meta]
|
||||
returns an empty collection.
|
||||
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
|
@ -10,7 +10,7 @@
|
|||
[nochunk]
|
||||
|
||||
This section delves deeper on how to use [reflink field] and [reflink field_view]
|
||||
and its underlying types. Please make sure you've read [link mysql.overview.rows_fields.fields this section]
|
||||
and its underlying types. Please make sure you've read [link mysql.overview.fields this section]
|
||||
before going on.
|
||||
|
||||
[section field_view vs field]
|
||||
|
@ -22,20 +22,20 @@ and [reflink field], which is owning. The relationship between them is similar t
|
|||
For efficiency reasons, all library functions return `field_view`s. For example:
|
||||
|
||||
```
|
||||
row r;
|
||||
// read it using resultset::read_one or a similar function
|
||||
field_view fv = r.at(0); // fv doesn't own its memory; if r goes out of scope, fv becomes invalid
|
||||
boost::string_view sv = fv.as_string(); // sv also points into r; if r goes out of scope, sv becomes invalid
|
||||
resultset result;
|
||||
conn.query("SELECT 'Hello world!'", result);
|
||||
field_view fv = result.rows().at(0).at(0); // fv doesn't own its memory; if result goes out of scope, fv becomes invalid
|
||||
boost::string_view sv = fv.as_string(); // sv also points into result; if result goes out of scope, sv becomes invalid
|
||||
```
|
||||
|
||||
When dealing with scalars (anything that is neither a string nor a blob), `field_view`'s accessors make
|
||||
a copy of the scalar:
|
||||
|
||||
```
|
||||
row r;
|
||||
// read it using resultset::read_one or a similar function
|
||||
field_view fv = r.at(0); // fv doesn't own its memory; if r goes out of scope, fv becomes invalid
|
||||
std::int64_t intv = fv.as_int64(); // intv is valid even after r goes out of scope
|
||||
resultset result;
|
||||
conn.query("SELECT 42", result);
|
||||
field_view fv = result.rows().at(0).at(0); // fv doesn't own its memory; if result goes out of scope, fv becomes invalid
|
||||
std::int64_t intv = fv.as_int64(); // intv is valid even after result goes out of scope
|
||||
```
|
||||
|
||||
`field_view`s are cheap to create and to copy, as they are small objects and don't perform
|
||||
|
@ -45,10 +45,10 @@ expensive to create and copy, as they may perform memory allocations.
|
|||
You may create a `field` from a `field_view`, taking ownership of its contents:
|
||||
|
||||
```
|
||||
row r;
|
||||
// read it using resultset::read_one or a similar function
|
||||
field_view fv = r.at(0); // fv doesn't own its memory; if r goes out of scope, fv becomes invalid
|
||||
field f (fv); // f takes ownership of fv's contents. f is valid even after r goes out of scope
|
||||
resultset result;
|
||||
conn.query("SELECT 'Hello world!'", result);
|
||||
field_view fv = result.rows().at(0).at(0); // fv doesn't own its memory; if result goes out of scope, fv becomes invalid
|
||||
field f (fv); // f takes ownership of fv's contents. f is valid even after result goes out of scope
|
||||
```
|
||||
|
||||
`field` and `field_view` use the same underlying types for scalars. For strings and blobs,
|
||||
|
|
|
@ -23,8 +23,8 @@ type always has one of the two folling forms:
|
|||
# `void(error_code)`. Used in operations that do
|
||||
not have a proper result, e.g. [refmem connection async_connect].
|
||||
# `void(error_code, T)`. Used in operations that
|
||||
have a result, e.g. [refmem resultset async_read_one]
|
||||
(in this case, `T` is `bool` for the "owning" version).
|
||||
have a result, e.g. [refmem connection async_read_one_row]
|
||||
(in this case, `T` is `row_view`).
|
||||
|
||||
As noted [link mysql.error_handling here], all asynchronous
|
||||
are overloaded to accept an optional [reflink error_info]
|
||||
|
@ -38,40 +38,11 @@ As mentioned in [link mysql.overview.async this section], only a single read and
|
|||
write operation per connection can be outstanding at a given point in time.
|
||||
If you need to perform queries in parallel, open more connections to the server.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:proxy_io Proxy I/O objects]
|
||||
|
||||
[reflink resultset] and [reflink statement] are proxy I/O objects. This means
|
||||
that they don't own a separate `Stream` object, but rather rely on an underlying
|
||||
[reflink connection] to perform I/O. This has the following implications:
|
||||
|
||||
* To use a `resultset` or a `statement` for operations that involve I/O, the
|
||||
`connection` object that created them must be alive and open.
|
||||
* Read and write operations on these objects count towards the limit of
|
||||
one concurrent read and write operation per connection.
|
||||
* I/O operations on these objects end up mutating the underlying `connection`'s
|
||||
state. In a multi-threaded environment, appropriate guards must be in place
|
||||
to avoid race conditions. This applies among all proxy I/O objects for a single connection;
|
||||
different connections share no state.
|
||||
Recall also that [reflink statement] objects are proxy I/O objects. All I/O operations
|
||||
on statements end up using the underlying connection's stream.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:ops Protocol operations]
|
||||
|
||||
The MySQL protocol is a half-duplex request/reply protocol that has the concept of "operations".
|
||||
This library models some operations as single functions (e.g. [refmem connection prepare_statement]),
|
||||
and splits other operations into several calls (e.g. [refmem connection query] +
|
||||
[refmem resultset read_all]), to provide more flexibility.
|
||||
|
||||
Once you engage in a multi-function operation, you must
|
||||
complete it (e.g. by calling `resultset::read_xxx` until all rows have been read) before engaging
|
||||
into the next one. Failing to do so will produce network packet mismatches, resulting in
|
||||
undefined behavior.
|
||||
|
||||
[endsect]
|
||||
|
||||
|
||||
[section:completion_tokens Completion tokens]
|
||||
|
||||
Any completion token you may use with Boost.Asio can also be used
|
||||
|
@ -83,7 +54,7 @@ with this library. Here are some of the most common:
|
|||
will be called when the operation completes. The initiating
|
||||
function will return `void`.
|
||||
|
||||
[link mysql.examples.query_async_callbacks This example]
|
||||
[link mysql.examples.async_callbacks This example]
|
||||
demonstrates asynchronous text queries with callbacks.
|
||||
* [*Futures]. In this case, you pass in the constant
|
||||
[asioreflink use_future use_future] as completion token.
|
||||
|
@ -98,7 +69,7 @@ with this library. Here are some of the most common:
|
|||
Note that the exception will [*not] contain the extra information
|
||||
stored in the [reflink error_info].
|
||||
|
||||
[link mysql.examples.query_async_futures This example]
|
||||
[link mysql.examples.async_futures This example]
|
||||
demonstrates using futures with async queries.
|
||||
* [*__Coroutine__ coroutines]. In this case, you pass in
|
||||
a [asioreflink yield_context yield_context]. To obtain one
|
||||
|
@ -116,7 +87,7 @@ with this library. Here are some of the most common:
|
|||
will not contain the extra information stored in the
|
||||
[reflink error_info].
|
||||
|
||||
[link mysql.examples.query_async_coroutines This example]
|
||||
[link mysql.examples.async_coroutines This example]
|
||||
uses __Coroutine__ coroutines with async queries.
|
||||
* [*C++20 coroutines]. In this case, you pass in the constant
|
||||
[asioreflink use_awaitable use_awaitable] as completion token.
|
||||
|
@ -131,7 +102,7 @@ with this library. Here are some of the most common:
|
|||
Note that this exception will not contain the extra
|
||||
information stored in the [reflink error_info].
|
||||
|
||||
[link mysql.examples.query_async_coroutinescpp20 This example]
|
||||
[link mysql.examples.async_coroutinescpp20 This example]
|
||||
demonstrates using C++20 coroutines to perform text
|
||||
queries.
|
||||
* Any other type that satisfies the __CompletionToken__ type requirements.
|
||||
|
|
|
@ -7,9 +7,8 @@
|
|||
|
||||
[section:other_streams UNIX sockets and other stream types]
|
||||
|
||||
All I/O objects ([reflink connection], [reflink statement]
|
||||
and [reflink resultset]) are templatized on the stream type.
|
||||
Any object fulfilling the __Stream__ concept may be used
|
||||
I/O objects ([reflink connection] and [reflink statement])
|
||||
are templatized on the stream type. Any object fulfilling the __Stream__ concept may be used
|
||||
as template argument.
|
||||
|
||||
[heading Convenience type aliases]
|
||||
|
@ -26,44 +25,32 @@ This library provides helper type aliases for the most common cases:
|
|||
[SSL over TCP]
|
||||
[`boost::asio::ssl::stream<boost::asio::ip::tcp::socket>`]
|
||||
[
|
||||
[reflink tcp_ssl_connection]
|
||||
|
||||
[reflink tcp_ssl_connection][br]
|
||||
[reflink tcp_ssl_statement]
|
||||
|
||||
[reflink tcp_ssl_resultset]
|
||||
]
|
||||
]
|
||||
[
|
||||
[Plaintext TCP]
|
||||
[`boost::asio::ip::tcp::socket`]
|
||||
[
|
||||
[reflink tcp_connection]
|
||||
|
||||
[reflink tcp_connection][br]
|
||||
[reflink tcp_statement]
|
||||
|
||||
[reflink tcp_resultset]
|
||||
]
|
||||
]
|
||||
[
|
||||
[SSL over UNIX sockets]
|
||||
[`boost::asio::ssl::stream<boost::asio::local::stream_protocol::socket>`]
|
||||
[
|
||||
[reflink unix_ssl_connection]
|
||||
|
||||
[reflink unix_ssl_connection][br]
|
||||
[reflink unix_ssl_statement]
|
||||
|
||||
[reflink unix_ssl_resultset]
|
||||
]
|
||||
]
|
||||
[
|
||||
[Plaintext UNIX sockets]
|
||||
[`boost::asio::local::stream_protocol::socket`]
|
||||
[
|
||||
[reflink unix_connection]
|
||||
|
||||
[reflink unix_connection][br]
|
||||
[reflink unix_statement]
|
||||
|
||||
[reflink unix_resultset]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
@ -75,7 +62,7 @@ Note that the UNIX socket type aliases only exist when the macro
|
|||
[link mysql.examples.unix_socket This example] employs a UNIX
|
||||
domain socket to establish a connection to a MySQL server.
|
||||
|
||||
[heading:connection Streams that are not sockets]
|
||||
[heading:non_sockets Streams that are not sockets]
|
||||
|
||||
When the `Stream` template argument for your `connection` fulfills
|
||||
the __SocketStream__ type requirements, you can use the member functions
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
[section:error_handling Error handling and available overloads]
|
||||
|
||||
This section describes the different error handling strategies
|
||||
you may use with __Self__, as well as the different overloads
|
||||
you may use with this library, as well as the different overloads
|
||||
available for each function involving network transfers.
|
||||
|
||||
The different overloads differ in how they deal with errors.
|
||||
|
@ -38,23 +38,14 @@ There are two overloads for each synchronous network function:
|
|||
|
||||
[heading Asynchronous functions]
|
||||
|
||||
The associated handler signature of all asynchronous functions
|
||||
has one of the following two forms:
|
||||
There are two overloads for each asynchronous network function:
|
||||
|
||||
* `void(error_code)`. Used in operations that do
|
||||
not have a proper result, e.g. [refmem connection async_connect].
|
||||
* `void(error_code, T)`. Used in operations that
|
||||
have a result, e.g. [refmem resultset async_read_one].
|
||||
* Without `error_info`, having the `CompletionToken` as last parameter.
|
||||
When they fail, they call the completion handler with a non-empty `error_code`.
|
||||
* With `error_info`, having an `error_info&` and `CompletionToken` as the last
|
||||
two parameters. When they fail, they set the `error_info` parameter to any server-provided
|
||||
diagnostic information, if available, and then call the completion handler with a non-empty `error_code`.
|
||||
|
||||
When asynchronous operations fail, they communicate it
|
||||
by calling the handler with a non-zero [reflink error_code].
|
||||
|
||||
If you are interested in also obtaining an [reflink error_info]
|
||||
when using asynchronous functions, there is an extra overload
|
||||
of each asynchronous function taking an additional output lvalue reference
|
||||
[reflink error_info] parameter. This parameter is set before
|
||||
calling the handler.
|
||||
|
||||
[heading Server-side and client-side error codes]
|
||||
|
||||
__Self__ [reflink error_code]s use [reflink errc] as the
|
||||
|
|
|
@ -101,8 +101,8 @@ underlying stream does not support SSL.
|
|||
|
||||
[refmem handshake_params buffer_config], of type [reflink buffer_params], lets you
|
||||
specify some defaults for internal buffer sizes. You can currently set the initial
|
||||
size of the read buffer, which can impact the performance of [refmem resultset read_some],
|
||||
as explained [link mysql.resultsets.read.read_some in this section].
|
||||
size of the read buffer, which can impact the performance of [refmem connection read_some_rows],
|
||||
as explained [link mysql.multi_function.read_some_rows in this section].
|
||||
|
||||
[endsect]
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ character set and collation. You can specify them in two ways:
|
|||
You can use [refmem connection query] to issue the statement:
|
||||
|
||||
```
|
||||
tcp_ssl_resultset result;
|
||||
resultset result;
|
||||
conn.query("SET NAMES utf8mb4", result);
|
||||
// Further operations can assume utf8mb4 as conn's charset
|
||||
```
|
||||
|
|
|
@ -13,15 +13,15 @@ setup] first.
|
|||
|
||||
Here is a list of available examples:
|
||||
|
||||
# [link mysql.examples.query_sync Text query, synchronous]
|
||||
# [link mysql.examples.text_queries Text queries]
|
||||
# [link mysql.examples.prepared_statements Prepared statements]
|
||||
# [link mysql.examples.metadata Metadata]
|
||||
# [link mysql.examples.unix_socket UNIX sockets]
|
||||
# [link mysql.examples.query_async_callbacks Text query, async with callbacks]
|
||||
# [link mysql.examples.query_async_futures Text query, async with futures]
|
||||
# [link mysql.examples.query_async_coroutines Text query, async with Boost.Coroutine coroutines]
|
||||
# [link mysql.examples.query_async_coroutinescpp20 Text query, async with C++20 coroutines]
|
||||
# [link mysql.examples.default_completion_tokens Default completion tokens]
|
||||
# [link mysql.examples.async_callbacks Async functions using callbacks]
|
||||
# [link mysql.examples.async_futures Async functions using futures]
|
||||
# [link mysql.examples.async_coroutines Async functions using Boost.Coroutine]
|
||||
# [link mysql.examples.async_coroutinescpp20 Async functions using C++20 coroutines]
|
||||
# [link mysql.examples.default_completion_tokens Async functions using default completion tokens]
|
||||
# [link mysql.examples.timeouts Settings timeouts]
|
||||
# [link mysql.examples.ssl Setting SSL options]
|
||||
|
||||
|
@ -48,27 +48,26 @@ which already ships with all the required configuration:
|
|||
Please note that this container is just for demonstrative purposes,
|
||||
and is not suitable for production.
|
||||
|
||||
The root MySQL user for these containers is `root` and it has an empty password.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:query_sync Text query, synchronous]
|
||||
[section:text_queries Text queries]
|
||||
|
||||
This example demonstrates how to connect a [reflink connection],
|
||||
how to issue a [link mysql.queries text query], and how to
|
||||
[link mysql.resultsets read a resultset]. It employs synchronous
|
||||
functions with exceptions as error handling. __see_error_handling__
|
||||
This example demonstrates how to issue text queries, without user-supplied parameters.
|
||||
It employs synchronous functions with exceptions as error handling. __see_error_handling__
|
||||
|
||||
__assume_setup__
|
||||
|
||||
[import ../../example/query_sync.cpp]
|
||||
[example_query_sync]
|
||||
[import ../../example/text_queries.cpp]
|
||||
[example_text_queries]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:prepared_statements Prepared statements]
|
||||
|
||||
This example demonstrates how to use [link mysql.prepared_statements
|
||||
prepared statements]. It employs synchronous functions with
|
||||
exceptions as error handling. __see_error_handling__
|
||||
This example demonstrates how to use prepared statements.
|
||||
It employs synchronous functions with exceptions as error handling. __see_error_handling__
|
||||
|
||||
__assume_setup__
|
||||
|
||||
|
@ -79,10 +78,8 @@ __assume_setup__
|
|||
|
||||
[section:metadata Metadata]
|
||||
|
||||
This example demonstrates how to use the available
|
||||
[link mysql.resultsets.metadata metadata] in a [reflink resultset].
|
||||
It employs synchronous functions with
|
||||
exceptions as error handling. __see_error_handling__
|
||||
This example demonstrates how to use the available metadata in a [reflink resultset].
|
||||
It employs synchronous functions with exceptions as error handling. __see_error_handling__
|
||||
|
||||
__assume_setup__
|
||||
|
||||
|
@ -109,74 +106,61 @@ __assume_setup__
|
|||
|
||||
[endsect]
|
||||
|
||||
[section:query_async_callbacks Text query, async with callbacks]
|
||||
[section:async_callbacks Async functions using callbacks]
|
||||
|
||||
This example demonstrates how to connect a [reflink connection],
|
||||
how to issue a [link mysql.queries text query], and how to
|
||||
[link mysql.resultsets read a resultset] using
|
||||
[link mysql.async asynchronous functions] with callbacks.
|
||||
This example demonstrates how use the asynchronous functions using callbacks.
|
||||
|
||||
__assume_setup__
|
||||
|
||||
[import ../../example/query_async_callbacks.cpp]
|
||||
[example_query_async_callbacks]
|
||||
[import ../../example/async_callbacks.cpp]
|
||||
[example_async_callbacks]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:query_async_futures Text query, async with futures]
|
||||
[section:async_futures Async functions using futures]
|
||||
|
||||
This example demonstrates how to connect a [reflink connection],
|
||||
how to issue a [link mysql.queries text query], and how to
|
||||
[link mysql.resultsets read a resultset] using
|
||||
[link mysql.async asynchronous functions] with futures
|
||||
([asioreflink use_future use_future]).
|
||||
This example demonstrates how use the asynchronous functions using futures.
|
||||
|
||||
__assume_setup__
|
||||
|
||||
[import ../../example/query_async_futures.cpp]
|
||||
[example_query_async_futures]
|
||||
[import ../../example/async_futures.cpp]
|
||||
[example_async_futures]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:query_async_coroutines Text query, async with Boost.Coroutine coroutines]
|
||||
[section:async_coroutines Async functions using Boost.Coroutine]
|
||||
|
||||
This example demonstrates how to connect a [reflink connection],
|
||||
how to issue a [link mysql.queries text query], and how to
|
||||
[link mysql.resultsets read a resultset] using
|
||||
[link mysql.async asynchronous functions] with __Coroutine__ coroutines
|
||||
This example demonstrates how use the asynchronous functions using __Coroutine__
|
||||
(using [asioreflink yield_context yield_context] and
|
||||
[asioreflink spawn spawn]).
|
||||
|
||||
__assume_setup__
|
||||
|
||||
[import ../../example/query_async_coroutines.cpp]
|
||||
[example_query_async_coroutines]
|
||||
[import ../../example/async_coroutines.cpp]
|
||||
[example_async_coroutines]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:query_async_coroutinescpp20 Text query, async with C++20 coroutines]
|
||||
[section:async_coroutinescpp20 Async functions using C++20 coroutines]
|
||||
|
||||
This example demonstrates how to connect a [reflink connection],
|
||||
how to issue a [link mysql.queries text query], and how to
|
||||
[link mysql.resultsets read a resultset] using
|
||||
[link mysql.async asynchronous functions] with C++20 coroutines
|
||||
([asioreflink use_awaitable use_awaitable] and [asioreflink
|
||||
This example demonstrates how use the asynchronous functions using C++20 coroutines
|
||||
(using [asioreflink use_awaitable use_awaitable] and [asioreflink
|
||||
co_spawn co_spawn]).
|
||||
|
||||
__assume_setup__
|
||||
|
||||
[import ../../example/query_async_coroutinescpp20.cpp]
|
||||
[example_query_async_coroutinescpp20]
|
||||
[import ../../example/async_coroutinescpp20.cpp]
|
||||
[example_async_coroutinescpp20]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:default_completion_tokens Default completion tokens]
|
||||
[section:default_completion_tokens Async functions using default completion tokens]
|
||||
|
||||
This example demonstrates how to use Boost.Asio's
|
||||
default completion token functionality with __Self__.
|
||||
For that purpose, it employs C++20 coroutines.
|
||||
If you are not familiar with them, look at
|
||||
[link mysql.examples.query_async_coroutinescpp20 this example]
|
||||
[link mysql.examples.async_coroutinescpp20 this example]
|
||||
first.
|
||||
|
||||
__assume_setup__
|
||||
|
@ -193,7 +177,7 @@ cancellation features to add timeouts to your async operations,
|
|||
including the ones provided by __Self__.
|
||||
For that purpose, it employs C++20 coroutines.
|
||||
If you are not familiar with them, look at
|
||||
[link mysql.examples.query_async_coroutinescpp20 this example]
|
||||
[link mysql.examples.async_coroutinescpp20 this example]
|
||||
first.
|
||||
|
||||
__assume_setup__
|
||||
|
@ -213,7 +197,7 @@ exceptions as error handling. __see_error_handling__
|
|||
|
||||
__assume_setup__ Additionally, you should run your MySQL server
|
||||
with some test certificates we created for you, just for this example.
|
||||
You can find them in this project's GitHub repository, under `BOOST_MYQL_ROOT/tools/ssl`.
|
||||
You can find them in this project's GitHub repository, under `tools/ssl`.
|
||||
If you're using the docker container, the setup has already been done
|
||||
for you.
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ Next, define the following environment variables:
|
|||
* If you are using MySQL 5.x or MariaDB, define `BOOST_MYSQL_NO_SHA256_TESTS=1`. These servers don't support part of the functionality we test.
|
||||
|
||||
If you are using `b2`, you can build the targets `boost/mysql/example//boost_mysql_all_examples`,
|
||||
`boost/mysql/test//boost_mysql_integrationtests` and `boost/mysql/test` to build and run the tests.
|
||||
`boost/mysql/test/integration//boost_mysql_integrationtests` and `boost/mysql/test` to build and run the tests.
|
||||
|
||||
If you are using `cmake`, add `-DBOOST_MYSQL_INTEGRATION_TESTS=ON` to enable building and running integration tests
|
||||
and examples, and then test regularly with `ctest`.
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
[/
|
||||
Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
|
||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
]
|
||||
|
||||
Query strings are assumed to be encoded using the connection's
|
||||
character set. You can set this value when establishing the
|
||||
connection (see [link mysql.connparams.collation this section])
|
||||
or change it using a __SET_NAMES__ statement.
|
|
@ -20,8 +20,8 @@
|
|||
<member><link linkend="mysql.ref.boost__mysql__connection">connection</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__date">date</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__datetime">datetime</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__execute_options">execute_options</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__error_info">error_info</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__execution_state">execution_state</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__field">field</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__field_view">field_view</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__handshake_params">handshake_params</link></member>
|
||||
|
@ -33,6 +33,7 @@
|
|||
<member><link linkend="mysql.ref.boost__mysql__rows">rows</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__rows_view">rows_view</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__statement">statement</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__statement_base">statement_base</link></member>
|
||||
</simplelist>
|
||||
</entry>
|
||||
<entry valign="top">
|
||||
|
@ -44,16 +45,12 @@
|
|||
<member><link linkend="mysql.ref.boost__mysql__time">time</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__error_code">error_code</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__tcp_connection">tcp_connection</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__tcp_resultset">tcp_resultset</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__tcp_statement">tcp_statement</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__tcp_ssl_connection">tcp_ssl_connection</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__tcp_ssl_resultset">tcp_ssl_resultset</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__tcp_ssl_statement">tcp_ssl_statement</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__unix_connection">unix_connection</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__unix_resultset">unix_resultset</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__unix_statement">unix_statement</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__unix_ssl_connection">unix_ssl_connection</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__unix_ssl_resultset">unix_ssl_resultset</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__unix_ssl_statement">unix_ssl_statement</link></member>
|
||||
</simplelist>
|
||||
</entry>
|
||||
|
@ -76,7 +73,6 @@
|
|||
<member><link linkend="mysql.ref.boost__mysql__min_datetime">min_datetime</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__max_time">max_time</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__min_time">min_time</link></member>
|
||||
<member><link linkend="mysql.ref.boost__mysql__no_statement_params">no_statement_params</link></member>
|
||||
</simplelist>
|
||||
</entry>
|
||||
<entry valign="top">
|
||||
|
|
|
@ -65,15 +65,14 @@ endif()
|
|||
|
||||
# Build and run all the examples
|
||||
add_example(tutorial TRUE ${SERVER_HOST})
|
||||
add_example(field TRUE ${SERVER_HOST})
|
||||
add_example(query_sync TRUE ${SERVER_HOST})
|
||||
add_example(query_async_callbacks TRUE ${SERVER_HOST})
|
||||
add_example(query_async_coroutines FALSE ${SERVER_HOST})
|
||||
add_example(query_async_coroutinescpp20 TRUE ${SERVER_HOST})
|
||||
add_example(query_async_futures TRUE ${SERVER_HOST})
|
||||
add_example(metadata TRUE ${SERVER_HOST})
|
||||
add_example(text_queries TRUE ${SERVER_HOST})
|
||||
add_example(prepared_statements TRUE ${SERVER_HOST})
|
||||
add_example(async_callbacks TRUE ${SERVER_HOST})
|
||||
add_example(async_coroutines FALSE ${SERVER_HOST})
|
||||
add_example(async_coroutinescpp20 TRUE ${SERVER_HOST})
|
||||
add_example(async_futures TRUE ${SERVER_HOST})
|
||||
add_example(default_completion_tokens TRUE ${SERVER_HOST})
|
||||
add_example(metadata TRUE ${SERVER_HOST})
|
||||
add_example(ssl TRUE ${SERVER_HOST})
|
||||
add_example(timeouts TRUE ${SERVER_HOST})
|
||||
if ("$ENV{BOOST_MYSQL_NO_UNIX_SOCKET_TESTS}" STREQUAL "")
|
||||
|
|
|
@ -17,15 +17,14 @@ if $(hostname) = ""
|
|||
# Example list
|
||||
local examples_no_unix =
|
||||
tutorial
|
||||
field
|
||||
query_sync
|
||||
query_async_callbacks
|
||||
query_async_coroutines
|
||||
query_async_coroutinescpp20
|
||||
query_async_futures
|
||||
metadata
|
||||
text_queries
|
||||
prepared_statements
|
||||
async_callbacks
|
||||
async_coroutines
|
||||
async_coroutinescpp20
|
||||
async_futures
|
||||
default_completion_tokens
|
||||
metadata
|
||||
ssl
|
||||
timeouts
|
||||
;
|
||||
|
@ -39,7 +38,7 @@ for local example in $(examples_no_unix)
|
|||
unit-test $(example_name)
|
||||
:
|
||||
"$(example).cpp"
|
||||
/boost/mysql//mysql
|
||||
/boost/mysql/test//mysql
|
||||
/boost/coroutine//boost_coroutine
|
||||
:
|
||||
<testing.arg>"example_user example_password $(hostname)"
|
||||
|
@ -53,7 +52,7 @@ if [ os.environ BOOST_MYSQL_NO_UNIX_SOCKET_TESTS ] = "" {
|
|||
unit-test boost_mysql_example_unix_socket
|
||||
:
|
||||
unix_socket.cpp
|
||||
/boost/mysql//mysql
|
||||
/boost/mysql/test//mysql
|
||||
:
|
||||
<testing.arg>"example_user example_password"
|
||||
;
|
||||
|
|
|
@ -5,12 +5,9 @@
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
//[example_query_async_callbacks
|
||||
//[example_async_callbacks
|
||||
|
||||
#include <boost/mysql.hpp>
|
||||
#include <boost/mysql/connection.hpp>
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/handshake_params.hpp>
|
||||
|
||||
#include <boost/asio/coroutine.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
|
@ -31,9 +28,9 @@ using boost::mysql::error_code;
|
|||
|
||||
void print_employee(boost::mysql::row_view employee)
|
||||
{
|
||||
std::cout << "Employee '" << employee[0] << " " // first_name (string)
|
||||
<< employee[1] << "' earns " // last_name (string)
|
||||
<< employee[2] << " dollars yearly\n"; // salary (double)
|
||||
std::cout << "Employee '" << employee.at(0) << " " // first_name (string)
|
||||
<< employee.at(1) << "' earns " // last_name (string)
|
||||
<< employee.at(2) << " dollars yearly\n"; // salary (double)
|
||||
}
|
||||
|
||||
void die_on_error(
|
||||
|
@ -56,16 +53,18 @@ class application
|
|||
boost::asio::ip::tcp::resolver resolver; // To perform hostname resolution
|
||||
boost::asio::ssl::context ssl_ctx; // MySQL 8+ default settings require SSL
|
||||
boost::mysql::tcp_ssl_connection conn; // Represents the connection to the MySQL server
|
||||
boost::mysql::tcp_ssl_resultset resultset; // A result from a query
|
||||
boost::mysql::rows rows; // The rows to be read from the resultset
|
||||
boost::mysql::tcp_ssl_statement stmt; // A prepared statement
|
||||
boost::mysql::resultset result; // A result from a query
|
||||
boost::mysql::error_info
|
||||
additional_info; // Will be populated with additional information about any errors
|
||||
additional_info; // Will be populated with additional information about any errors
|
||||
const char* company_id; // The ID of the company whose employees we want to list. Untrusted.
|
||||
public:
|
||||
application(const char* username, const char* password)
|
||||
application(const char* username, const char* password, const char* company_id)
|
||||
: conn_params(username, password, "boost_mysql_examples"),
|
||||
resolver(ctx.get_executor()),
|
||||
ssl_ctx(boost::asio::ssl::context::tls_client),
|
||||
conn(ctx, ssl_ctx)
|
||||
conn(ctx, ssl_ctx),
|
||||
company_id(company_id)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -88,49 +87,40 @@ public:
|
|||
{
|
||||
conn.async_connect(*eps.begin(), conn_params, additional_info, [this](error_code err) {
|
||||
die_on_error(err, additional_info);
|
||||
query_employees();
|
||||
prepare_statement();
|
||||
});
|
||||
}
|
||||
|
||||
void prepare_statement()
|
||||
{
|
||||
// We will be using company_id, which is untrusted user input, so we will use a prepared
|
||||
// statement.
|
||||
conn.async_prepare_statement(
|
||||
"SELECT first_name, last_name, salary FROM employee WHERE company_id = ?",
|
||||
stmt,
|
||||
additional_info,
|
||||
[this](error_code err) {
|
||||
die_on_error(err, additional_info);
|
||||
query_employees();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void query_employees()
|
||||
{
|
||||
const char*
|
||||
sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
|
||||
conn.async_query(sql, resultset, additional_info, [this](error_code err) {
|
||||
die_on_error(err, additional_info);
|
||||
resultset.async_read_all(rows, additional_info, [this](error_code err) {
|
||||
stmt.async_execute(
|
||||
std::make_tuple(company_id),
|
||||
result,
|
||||
additional_info,
|
||||
[this](error_code err) {
|
||||
die_on_error(err, additional_info);
|
||||
for (const auto& employee : rows)
|
||||
for (boost::mysql::row_view employee : result.rows())
|
||||
{
|
||||
print_employee(employee);
|
||||
}
|
||||
update_slacker();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void update_slacker()
|
||||
{
|
||||
const char* sql = "UPDATE employee SET salary = 15000 WHERE last_name = 'Slacker'";
|
||||
conn.async_query(sql, resultset, additional_info, [this](error_code err) {
|
||||
die_on_error(err, additional_info);
|
||||
ASSERT(resultset.complete()); // an UPDATE never returns rows
|
||||
query_intern();
|
||||
});
|
||||
}
|
||||
|
||||
void query_intern()
|
||||
{
|
||||
const char* sql = "SELECT salary FROM employee WHERE last_name = 'Slacker'";
|
||||
conn.async_query(sql, resultset, additional_info, [this](error_code err) {
|
||||
die_on_error(err, additional_info);
|
||||
resultset.async_read_all(rows, additional_info, [this](error_code err) {
|
||||
die_on_error(err, additional_info);
|
||||
double salary = rows.at(0).at(0).as_double();
|
||||
ASSERT(salary == 15000);
|
||||
close();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void close()
|
||||
|
@ -146,13 +136,18 @@ public:
|
|||
|
||||
void main_impl(int argc, char** argv)
|
||||
{
|
||||
if (argc != 4)
|
||||
if (argc != 4 && argc != 5)
|
||||
{
|
||||
std::cerr << "Usage: " << argv[0] << " <username> <password> <server-hostname>\n";
|
||||
std::cerr << "Usage: " << argv[0]
|
||||
<< " <username> <password> <server-hostname> [company-id]\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
application app(argv[1], argv[2]);
|
||||
// The company_id whose employees we will be listing. This
|
||||
// is user-supplied input, and should be treated as untrusted.
|
||||
const char* company_id = argc == 5 ? argv[4] : "HGS";
|
||||
|
||||
application app(argv[1], argv[2], company_id);
|
||||
app.start(argv[3]); // starts the async chain
|
||||
app.run(); // run the asio::io_context until the async chain finishes
|
||||
}
|
|
@ -5,10 +5,9 @@
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
//[example_query_async_coroutines
|
||||
//[example_async_coroutines
|
||||
|
||||
#include <boost/mysql.hpp>
|
||||
#include <boost/mysql/handshake_params.hpp>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
|
@ -23,9 +22,9 @@ using boost::mysql::error_info;
|
|||
|
||||
void print_employee(boost::mysql::row_view employee)
|
||||
{
|
||||
std::cout << "Employee '" << employee[0] << " " // first_name (string)
|
||||
<< employee[1] << "' earns " // last_name (string)
|
||||
<< employee[2] << " dollars yearly\n"; // salary (double)
|
||||
std::cout << "Employee '" << employee.at(0) << " " // first_name (string)
|
||||
<< employee.at(1) << "' earns " // last_name (string)
|
||||
<< employee.at(2) << " dollars yearly\n"; // salary (double)
|
||||
}
|
||||
|
||||
// Throws an exception if an operation failed
|
||||
|
@ -37,14 +36,19 @@ void check_error(const error_code& err, const error_info& info = {})
|
|||
|
||||
void main_impl(int argc, char** argv)
|
||||
{
|
||||
if (argc != 4)
|
||||
if (argc != 4 && argc != 5)
|
||||
{
|
||||
std::cerr << "Usage: " << argv[0] << " <username> <password> <server-hostname>\n";
|
||||
std::cerr << "Usage: " << argv[0]
|
||||
<< " <username> <password> <server-hostname> [company-id]\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const char* hostname = argv[3];
|
||||
|
||||
// The company_id whose employees we will be listing. This
|
||||
// is user-supplied input, and should be treated as untrusted.
|
||||
const char* company_id = argc == 5 ? argv[4] : "HGS";
|
||||
|
||||
// I/O context and connection. We use SSL because MySQL 8+ default settings require it.
|
||||
boost::asio::io_context ctx;
|
||||
boost::asio::ssl::context ssl_ctx(boost::asio::ssl::context::tls_client);
|
||||
|
@ -70,7 +74,7 @@ void main_impl(int argc, char** argv)
|
|||
*/
|
||||
boost::asio::spawn(
|
||||
ctx.get_executor(),
|
||||
[&conn, &resolver, params, hostname](boost::asio::yield_context yield) {
|
||||
[&conn, &resolver, params, hostname, company_id](boost::asio::yield_context yield) {
|
||||
// This error_code and error_info will be filled if an
|
||||
// operation fails. We will check them for every operation we perform.
|
||||
boost::mysql::error_code ec;
|
||||
|
@ -88,26 +92,26 @@ void main_impl(int argc, char** argv)
|
|||
conn.async_connect(*endpoints.begin(), params, additional_info, yield[ec]);
|
||||
check_error(ec);
|
||||
|
||||
// Issue the query to the server
|
||||
const char*
|
||||
sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
|
||||
boost::mysql::tcp_ssl_resultset result;
|
||||
conn.async_query(sql, result, additional_info, yield[ec]);
|
||||
// We will be using company_id, which is untrusted user input, so we will use a prepared
|
||||
// statement.
|
||||
boost::mysql::tcp_ssl_statement stmt;
|
||||
conn.async_prepare_statement(
|
||||
"SELECT first_name, last_name, salary FROM employee WHERE company_id = ?",
|
||||
stmt,
|
||||
additional_info,
|
||||
yield[ec]
|
||||
);
|
||||
check_error(ec, additional_info);
|
||||
|
||||
/**
|
||||
* Get all rows in the resultset. We will employ resultset::async_read_one(),
|
||||
* which reads a single row at every call. resultset::complete() returns true only after
|
||||
* we've read the last row in the resultset.
|
||||
*/
|
||||
boost::mysql::row row;
|
||||
while (true)
|
||||
// Execute the statement
|
||||
boost::mysql::resultset result;
|
||||
stmt.async_execute(std::make_tuple(company_id), result, additional_info, yield[ec]);
|
||||
check_error(ec, additional_info);
|
||||
|
||||
// Print the employees
|
||||
for (boost::mysql::row_view employee : result.rows())
|
||||
{
|
||||
result.async_read_one(row, additional_info, yield[ec]);
|
||||
check_error(ec, additional_info);
|
||||
if (result.complete())
|
||||
break;
|
||||
print_employee(row);
|
||||
print_employee(employee);
|
||||
}
|
||||
|
||||
// Notify the MySQL server we want to quit, then close the underlying connection.
|
|
@ -5,11 +5,9 @@
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
//[example_query_async_coroutinescpp20
|
||||
//[example_async_coroutinescpp20
|
||||
|
||||
#include <boost/mysql.hpp>
|
||||
#include <boost/mysql/handshake_params.hpp>
|
||||
#include <boost/mysql/tcp_ssl.hpp>
|
||||
|
||||
#include <boost/asio/awaitable.hpp>
|
||||
#include <boost/asio/co_spawn.hpp>
|
||||
|
@ -27,11 +25,13 @@ using boost::mysql::error_code;
|
|||
|
||||
#ifdef BOOST_ASIO_HAS_CO_AWAIT
|
||||
|
||||
using boost::asio::use_awaitable;
|
||||
|
||||
void print_employee(boost::mysql::row_view employee)
|
||||
{
|
||||
std::cout << "Employee '" << employee[0] << " " // first_name (string)
|
||||
<< employee[1] << "' earns " // last_name (string)
|
||||
<< employee[2] << " dollars yearly\n"; // salary (double)
|
||||
std::cout << "Employee '" << employee.at(0) << " " // first_name (string)
|
||||
<< employee.at(1) << "' earns " // last_name (string)
|
||||
<< employee.at(2) << " dollars yearly\n"; // salary (double)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -46,7 +46,7 @@ void print_employee(boost::mysql::row_view employee)
|
|||
* all information it needs for resuming. When the asynchronous operation completes,
|
||||
* the coroutine will resume in the point it was left.
|
||||
*
|
||||
* The return type of an asynchronous operation that uses boost::asio::use_awaitable
|
||||
* The return type of an asynchronous operation that uses use_awaitable
|
||||
* as completion token is a boost::asio::awaitable<T>, where T
|
||||
* is the second argument to the handler signature for the asynchronous operation.
|
||||
* If any of the asynchronous operations fail, an exception will be raised
|
||||
|
@ -56,7 +56,8 @@ boost::asio::awaitable<void> start_query(
|
|||
boost::mysql::tcp_ssl_connection& conn,
|
||||
boost::asio::ip::tcp::resolver& resolver,
|
||||
const boost::mysql::handshake_params& params,
|
||||
const char* hostname
|
||||
const char* hostname,
|
||||
const char* company_id
|
||||
)
|
||||
{
|
||||
try
|
||||
|
@ -65,34 +66,33 @@ boost::asio::awaitable<void> start_query(
|
|||
auto endpoints = co_await resolver.async_resolve(
|
||||
hostname,
|
||||
boost::mysql::default_port_string,
|
||||
boost::asio::use_awaitable
|
||||
use_awaitable
|
||||
);
|
||||
|
||||
// Connect to server
|
||||
co_await conn.async_connect(*endpoints.begin(), params, boost::asio::use_awaitable);
|
||||
co_await conn.async_connect(*endpoints.begin(), params, use_awaitable);
|
||||
|
||||
/**
|
||||
* Issue the query to the server. Note that async_query returns a
|
||||
* boost::asio::awaitable<boost::mysql::tcp_ssl_resultset>.
|
||||
*/
|
||||
const char*
|
||||
sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
|
||||
boost::mysql::tcp_ssl_resultset result;
|
||||
co_await conn.async_query(sql, result, boost::asio::use_awaitable);
|
||||
// We will be using company_id, which is untrusted user input, so we will use a prepared
|
||||
// statement.
|
||||
boost::mysql::tcp_ssl_statement stmt;
|
||||
co_await conn.async_prepare_statement(
|
||||
"SELECT first_name, last_name, salary FROM employee WHERE company_id = ?",
|
||||
stmt,
|
||||
use_awaitable
|
||||
);
|
||||
|
||||
/**
|
||||
* Get all rows in the resultset. We will employ resultset::async_read_one(),
|
||||
* which reads a single row at every call. resultset::complete() returns true only after
|
||||
* we've read the last row in the resultset.
|
||||
*/
|
||||
boost::mysql::row row;
|
||||
while (co_await result.async_read_one(row, boost::asio::use_awaitable))
|
||||
// Execute the statement
|
||||
boost::mysql::resultset result;
|
||||
co_await stmt.async_execute(std::make_tuple(company_id), result, use_awaitable);
|
||||
|
||||
// Print all employees
|
||||
for (boost::mysql::row_view employee : result.rows())
|
||||
{
|
||||
print_employee(row);
|
||||
print_employee(employee);
|
||||
}
|
||||
|
||||
// Notify the MySQL server we want to quit, then close the underlying connection.
|
||||
co_await conn.async_close(boost::asio::use_awaitable);
|
||||
co_await conn.async_close(use_awaitable);
|
||||
}
|
||||
catch (const boost::system::system_error& err)
|
||||
{
|
||||
|
@ -106,14 +106,19 @@ boost::asio::awaitable<void> start_query(
|
|||
|
||||
void main_impl(int argc, char** argv)
|
||||
{
|
||||
if (argc != 4)
|
||||
if (argc != 4 && argc != 5)
|
||||
{
|
||||
std::cerr << "Usage: " << argv[0] << " <username> <password> <server-hostname>\n";
|
||||
std::cerr << "Usage: " << argv[0]
|
||||
<< " <username> <password> <server-hostname> [company-id]\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const char* hostname = argv[3];
|
||||
|
||||
// The company_id whose employees we will be listing. This
|
||||
// is user-supplied input, and should be treated as untrusted.
|
||||
const char* company_id = argc == 5 ? argv[4] : "HGS";
|
||||
|
||||
// I/O context and connection. We use SSL because MySQL 8+ default settings require it.
|
||||
boost::asio::io_context ctx;
|
||||
boost::asio::ssl::context ssl_ctx(boost::asio::ssl::context::tls_client);
|
||||
|
@ -136,13 +141,13 @@ void main_impl(int argc, char** argv)
|
|||
*/
|
||||
boost::asio::co_spawn(
|
||||
ctx.get_executor(),
|
||||
[&conn, &resolver, params, hostname] {
|
||||
return start_query(conn, resolver, params, hostname);
|
||||
[&conn, &resolver, params, hostname, company_id] {
|
||||
return start_query(conn, resolver, params, hostname, company_id);
|
||||
},
|
||||
boost::asio::detached
|
||||
);
|
||||
|
||||
// Calling run will actually start the requested operations.
|
||||
// Calling run will execute the requested operations.
|
||||
ctx.run();
|
||||
}
|
||||
|
|
@ -5,10 +5,13 @@
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
//[example_query_async_futures
|
||||
//[example_async_futures
|
||||
|
||||
#include <boost/mysql/handshake_params.hpp>
|
||||
#include <boost/mysql/row_view.hpp>
|
||||
#include <boost/mysql/tcp_ssl.hpp>
|
||||
|
||||
#include <boost/mysql.hpp>
|
||||
#include <boost/mysql/handshake_params.hpp>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ssl/context.hpp>
|
||||
|
@ -23,9 +26,9 @@ using boost::mysql::error_code;
|
|||
|
||||
void print_employee(boost::mysql::row_view employee)
|
||||
{
|
||||
std::cout << "Employee '" << employee[0] << " " // first_name (string)
|
||||
<< employee[1] << "' earns " // last_name (string)
|
||||
<< employee[2] << " dollars yearly\n"; // salary (double)
|
||||
std::cout << "Employee '" << employee.at(0) << " " // first_name (string)
|
||||
<< employee.at(1) << "' earns " // last_name (string)
|
||||
<< employee.at(2) << " dollars yearly\n"; // salary (double)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -58,12 +61,17 @@ public:
|
|||
|
||||
void main_impl(int argc, char** argv)
|
||||
{
|
||||
if (argc != 4)
|
||||
if (argc != 4 && argc != 5)
|
||||
{
|
||||
std::cerr << "Usage: " << argv[0] << " <username> <password> <server-hostname>\n";
|
||||
std::cerr << "Usage: " << argv[0]
|
||||
<< " <username> <password> <server-hostname> [company-id]\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// The company_id whose employees we will be listing. This
|
||||
// is user-supplied input, and should be treated as untrusted.
|
||||
const char* company_id = argc == 5 ? argv[4] : "HGS";
|
||||
|
||||
// Context and connections
|
||||
application app; // boost::asio::io_context and a thread that calls run()
|
||||
boost::asio::ssl::context ssl_ctx(boost::asio::ssl::context::tls_client);
|
||||
|
@ -96,21 +104,25 @@ void main_impl(int argc, char** argv)
|
|||
std::future<void> fut = conn.async_connect(*endpoints.begin(), params, use_future);
|
||||
fut.get();
|
||||
|
||||
// Issue the query to the server
|
||||
const char* sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
|
||||
boost::mysql::tcp_ssl_resultset result;
|
||||
fut = conn.async_query(sql, result, use_future);
|
||||
// We will be using company_id, which is untrusted user input, so we will use a prepared
|
||||
// statement.
|
||||
boost::mysql::tcp_ssl_statement stmt;
|
||||
fut = conn.async_prepare_statement(
|
||||
"SELECT first_name, last_name, salary FROM employee WHERE company_id = ?",
|
||||
stmt,
|
||||
use_future
|
||||
);
|
||||
fut.get();
|
||||
|
||||
/**
|
||||
* Get all rows in the resultset. We will employ resultset::async_read_one(),
|
||||
* which reads a single row at every call. resultset::complete() returns true only after we've
|
||||
* read the last row in the resultset.
|
||||
*/
|
||||
boost::mysql::row row;
|
||||
while (result.async_read_one(row, use_future).get())
|
||||
// Execute the statement
|
||||
boost::mysql::resultset result;
|
||||
fut = stmt.async_execute(std::make_tuple(company_id), result, use_future);
|
||||
fut.get();
|
||||
|
||||
// Print employees
|
||||
for (boost::mysql::row_view employee : result.rows())
|
||||
{
|
||||
print_employee(row);
|
||||
print_employee(employee);
|
||||
}
|
||||
|
||||
// Notify the MySQL server we want to quit, then close the underlying connection.
|
|
@ -6,9 +6,8 @@
|
|||
//
|
||||
|
||||
//[example_default_completion_tokens]
|
||||
|
||||
#include <boost/mysql.hpp>
|
||||
#include <boost/mysql/handshake_params.hpp>
|
||||
#include <boost/mysql/row_view.hpp>
|
||||
|
||||
#include <boost/asio/awaitable.hpp>
|
||||
#include <boost/asio/basic_stream_socket.hpp>
|
||||
|
@ -29,9 +28,9 @@ using boost::mysql::error_code;
|
|||
|
||||
void print_employee(boost::mysql::row_view employee)
|
||||
{
|
||||
std::cout << "Employee '" << employee[0] << " " // first_name (string)
|
||||
<< employee[1] << "' earns " // last_name (string)
|
||||
<< employee[2] << " dollars yearly\n"; // salary (double)
|
||||
std::cout << "Employee '" << employee.at(0) << " " // first_name (string)
|
||||
<< employee.at(1) << "' earns " // last_name (string)
|
||||
<< employee.at(2) << " dollars yearly\n"; // salary (double)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -68,7 +67,8 @@ boost::asio::awaitable<void, base_executor_type> start_query(
|
|||
connection_type& conn,
|
||||
resolver_type& resolver,
|
||||
const char* hostname,
|
||||
const boost::mysql::handshake_params& params
|
||||
const boost::mysql::handshake_params& params,
|
||||
const char* company_id
|
||||
)
|
||||
{
|
||||
try
|
||||
|
@ -82,26 +82,25 @@ boost::asio::awaitable<void, base_executor_type> start_query(
|
|||
// Connect to server
|
||||
co_await conn.async_connect(*endpoints.begin(), params);
|
||||
|
||||
/**
|
||||
* Issue the query to the server. Note that the resultset type won't be
|
||||
* tcp_ssl_resultset, because the stream type we are using is different.
|
||||
*/
|
||||
const char*
|
||||
sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
|
||||
connection_type::resultset_type result;
|
||||
co_await conn.async_query(sql, result);
|
||||
// Prepare an statement. Note that the statement type won't be
|
||||
// tcp_ssl_statement, because the stream type we are using is different.
|
||||
// We can use connection::statement_type to help
|
||||
connection_type::statement_type stmt;
|
||||
co_await conn.async_prepare_statement(
|
||||
"SELECT first_name, last_name, salary FROM employee WHERE company_id = ?",
|
||||
stmt
|
||||
);
|
||||
|
||||
/**
|
||||
* Get all rows in the resultset. We will employ resultset::async_read_one(),
|
||||
* which reads a single row at every call and returns true if a row was read successfully.
|
||||
*/
|
||||
boost::mysql::row row;
|
||||
while (co_await result.async_read_one(row))
|
||||
// Execute it
|
||||
boost::mysql::resultset result;
|
||||
co_await stmt.async_execute(std::make_tuple(company_id), result);
|
||||
for (boost::mysql::row_view employee : result.rows())
|
||||
{
|
||||
print_employee(row);
|
||||
print_employee(employee);
|
||||
}
|
||||
|
||||
// Notify the MySQL server we want to quit, then close the underlying connection.
|
||||
// This will also deallocate the statement from the server.
|
||||
co_await conn.async_close();
|
||||
}
|
||||
catch (const boost::system::system_error& err)
|
||||
|
@ -116,13 +115,15 @@ boost::asio::awaitable<void, base_executor_type> start_query(
|
|||
|
||||
void main_impl(int argc, char** argv)
|
||||
{
|
||||
if (argc != 4)
|
||||
if (argc != 4 && argc != 5)
|
||||
{
|
||||
std::cerr << "Usage: " << argv[0] << " <username> <password> <server-hostname>\n";
|
||||
std::cerr << "Usage: " << argv[0]
|
||||
<< " <username> <password> <server-hostname> [company-id]\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const char* hostname = argv[3];
|
||||
const char* company_id = argc == 5 ? argv[4] : "HGS";
|
||||
|
||||
// I/O context and connection. We use SSL because MySQL 8+ default settings require it.
|
||||
boost::asio::io_context ctx;
|
||||
|
@ -145,8 +146,8 @@ void main_impl(int argc, char** argv)
|
|||
*/
|
||||
boost::asio::co_spawn(
|
||||
ctx.get_executor(),
|
||||
[&conn, &resolver, hostname, params] {
|
||||
return start_query(conn, resolver, hostname, params);
|
||||
[&conn, &resolver, hostname, params, company_id] {
|
||||
return start_query(conn, resolver, hostname, params, company_id);
|
||||
},
|
||||
boost::asio::detached
|
||||
);
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <boost/mysql/field_view.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#define ASSERT(expr) \
|
||||
if (!(expr)) \
|
||||
{ \
|
||||
std::cerr << "Assertion failed: " #expr << std::endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
void example_as()
|
||||
{
|
||||
//[value_example_get
|
||||
boost::mysql::field_view v("hello"); // v contains type boost::string_view
|
||||
boost::string_view typed_val = v.as_string(); // retrieves the underlying string
|
||||
ASSERT(typed_val == "hello");
|
||||
try
|
||||
{
|
||||
v.as_double(); // wrong type! throws boost::mysql::bad_field_access
|
||||
// v.get_double(); // don't do this - UB
|
||||
}
|
||||
catch (const boost::mysql::bad_field_access&)
|
||||
{
|
||||
}
|
||||
//]
|
||||
}
|
||||
|
||||
void example_is()
|
||||
{
|
||||
//[value_example_is
|
||||
boost::mysql::field_view v(std::uint64_t(42)); // v contains type std::uint64_t
|
||||
ASSERT(v.is_uint64()); // exact type match
|
||||
ASSERT(!v.is_int64()); // the underlying type is unsigned
|
||||
ASSERT(!v.is_string());
|
||||
//]
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
example_as();
|
||||
example_is();
|
||||
}
|
|
@ -8,8 +8,6 @@
|
|||
//[example_metadata
|
||||
|
||||
#include <boost/mysql.hpp>
|
||||
#include <boost/mysql/connection.hpp>
|
||||
#include <boost/mysql/handshake_params.hpp>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ssl/context.hpp>
|
||||
|
@ -56,7 +54,7 @@ void main_impl(int argc, char** argv)
|
|||
FROM employee emp
|
||||
JOIN company comp ON (comp.id = emp.company_id)
|
||||
)";
|
||||
boost::mysql::tcp_ssl_resultset result;
|
||||
boost::mysql::resultset result;
|
||||
conn.query(sql, result);
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,14 +8,13 @@
|
|||
//[example_prepared_statements
|
||||
|
||||
#include <boost/mysql.hpp>
|
||||
#include <boost/mysql/field_view.hpp>
|
||||
#include <boost/mysql/handshake_params.hpp>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ssl/context.hpp>
|
||||
#include <boost/system/system_error.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <tuple>
|
||||
|
||||
#define ASSERT(expr) \
|
||||
|
@ -25,14 +24,27 @@
|
|||
exit(1); \
|
||||
}
|
||||
|
||||
double generate_random_payrise()
|
||||
{
|
||||
std::random_device dev;
|
||||
std::default_random_engine eng(dev());
|
||||
std::uniform_real_distribution<> dist(500.0, 3000.0);
|
||||
return dist(eng);
|
||||
}
|
||||
|
||||
void main_impl(int argc, char** argv)
|
||||
{
|
||||
if (argc != 4)
|
||||
if (argc != 4 && argc != 5)
|
||||
{
|
||||
std::cerr << "Usage: " << argv[0] << " <username> <password> <server-hostname>\n";
|
||||
std::cerr << "Usage: " << argv[0]
|
||||
<< " <username> <password> <server-hostname> [employee-first-name]\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// The first_name of the employee we will be working with. This
|
||||
// is user-supplied input, and should be treated as untrusted.
|
||||
const char* first_name = argc == 5 ? argv[4] : "Efficient";
|
||||
|
||||
// I/O context and connection. We use SSL because MySQL 8+ default settings require it.
|
||||
boost::asio::io_context ctx;
|
||||
boost::asio::ssl::context ssl_ctx(boost::asio::ssl::context::tls_client);
|
||||
|
@ -70,66 +82,51 @@ void main_impl(int argc, char** argv)
|
|||
* We prepare two statements, a SELECT and an UPDATE.
|
||||
*/
|
||||
//[prepared_statements_prepare
|
||||
const char* salary_getter_sql = "SELECT salary FROM employee WHERE first_name = ?";
|
||||
boost::mysql::tcp_ssl_statement salary_getter;
|
||||
conn.prepare_statement(salary_getter_sql, salary_getter);
|
||||
conn.prepare_statement("SELECT salary FROM employee WHERE first_name = ?", salary_getter);
|
||||
//]
|
||||
|
||||
// num_params() returns the number of parameters (question marks)
|
||||
ASSERT(salary_getter.num_params() == 1);
|
||||
|
||||
const char* salary_updater_sql = "UPDATE employee SET salary = ? WHERE first_name = ?";
|
||||
boost::mysql::tcp_ssl_statement salary_updater;
|
||||
conn.prepare_statement(salary_updater_sql, salary_updater);
|
||||
conn.prepare_statement(
|
||||
"UPDATE employee SET salary = salary + ? WHERE first_name = ?",
|
||||
salary_updater
|
||||
);
|
||||
ASSERT(salary_updater.num_params() == 2);
|
||||
|
||||
// TODO: update this
|
||||
/*
|
||||
* Once a statement has been prepared, it can be executed as many times as
|
||||
* desired, by calling statement::execute(). execute takes as input a
|
||||
* (possibly empty) collection of boost::mysql::value's and returns a resultset (by lvalue
|
||||
* reference). The returned resultset works the same as the one returned by connection::query().
|
||||
*
|
||||
* The parameters passed to execute() are replaced in the order of declaration:
|
||||
* the first question mark will be replaced by the first passed parameter,
|
||||
* the second question mark by the second parameter and so on. The number
|
||||
* of passed parameters must match exactly the number of parameters for
|
||||
* the prepared statement.
|
||||
*
|
||||
* Any collection providing member functions begin() and end() returning
|
||||
* forward iterators to boost::mysql::field_view's is acceptable. We use
|
||||
* boost::mysql::make_field_views(), which creates a std::array with the passed in values
|
||||
* converted to boost::mysql::field_view's.
|
||||
* desired, by calling statement::execute(). Parameter actual values are provided
|
||||
* as a std::tuple. Executing a statement yields a resultset.
|
||||
*/
|
||||
//[prepared_statements_execute
|
||||
boost::mysql::tcp_ssl_resultset result;
|
||||
salary_getter.execute(std::make_tuple("Efficient"), result);
|
||||
|
||||
boost::mysql::rows salaries;
|
||||
result.read_all(salaries); // Get all the results
|
||||
boost::mysql::resultset select_result, update_result;
|
||||
salary_getter.execute(std::make_tuple(first_name), select_result);
|
||||
//]
|
||||
ASSERT(salaries.size() == 1);
|
||||
double salary = salaries[0].at(0).as_double(); // First row, first column, cast to double
|
||||
std::cout << "The salary before the payrise was: " << salary << std::endl;
|
||||
|
||||
// First row, first column, cast to double
|
||||
double old_salary = select_result.rows().at(0).at(0).as_double();
|
||||
std::cout << "The salary before the payrise was: " << old_salary << std::endl;
|
||||
|
||||
// Run the update. In this case, we must pass in two parameters.
|
||||
salary_updater.execute(std::make_tuple(35000.0, "Efficient"), result);
|
||||
ASSERT(result.complete()); // an UPDATE never returns rows
|
||||
double payrise = generate_random_payrise();
|
||||
salary_updater.execute(std::make_tuple(payrise, first_name), update_result);
|
||||
ASSERT(update_result.rows().empty()); // an UPDATE never returns rows
|
||||
|
||||
/**
|
||||
* Execute the select again. We can execute a prepared statement
|
||||
* as many times as we want. We do NOT need to call
|
||||
* connection::prepare_statement() again.
|
||||
*/
|
||||
salary_getter.execute(std::make_tuple("Efficient"), result);
|
||||
result.read_all(salaries);
|
||||
salary = salaries.at(0).at(0).as_double();
|
||||
ASSERT(salary == 35000); // Our update took place, and the dev got his pay rise
|
||||
std::cout << "The salary after the payrise was: " << salary << std::endl;
|
||||
salary_getter.execute(std::make_tuple(first_name), select_result);
|
||||
double new_salary = select_result.rows().at(0).at(0).as_double();
|
||||
ASSERT(new_salary > old_salary); // Our update took place
|
||||
std::cout << "The salary after the payrise was: " << new_salary << std::endl;
|
||||
|
||||
/**
|
||||
* Close the statements. Closing a statement deallocates it from the server.
|
||||
* Once a statement is closed, trying to execute it will return an error.
|
||||
*
|
||||
* Closing statements implies communicating with the server and can thus fail.
|
||||
*
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
//
|
||||
|
||||
//[example_ssl
|
||||
|
||||
#include <boost/mysql.hpp>
|
||||
#include <boost/mysql/handshake_params.hpp>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ssl/context.hpp>
|
||||
|
@ -49,9 +49,9 @@ OzBrmpfHEhF6NDU=
|
|||
|
||||
void print_employee(boost::mysql::row_view employee)
|
||||
{
|
||||
std::cout << "Employee '" << employee[0] << " " // first_name (string)
|
||||
<< employee[1] << "' earns " // last_name (string)
|
||||
<< employee[2] << " dollars yearly\n"; // salary (double)
|
||||
std::cout << "Employee '" << employee.at(0) << " " // first_name (string)
|
||||
<< employee.at(1) << "' earns " // last_name (string)
|
||||
<< employee.at(2) << " dollars yearly\n"; // salary (double)
|
||||
}
|
||||
|
||||
void main_impl(int argc, char** argv)
|
||||
|
@ -108,13 +108,11 @@ void main_impl(int argc, char** argv)
|
|||
conn.connect(*endpoints.begin(), params);
|
||||
|
||||
// We can now use the connection as we would normally do.
|
||||
const char* sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
|
||||
boost::mysql::tcp_ssl_resultset result;
|
||||
const char* sql = "SELECT first_name, last_name, salary FROM employee";
|
||||
boost::mysql::resultset result;
|
||||
conn.query(sql, result);
|
||||
|
||||
boost::mysql::rows employees;
|
||||
result.read_all(employees);
|
||||
for (const auto& employee : employees)
|
||||
for (auto employee : result.rows())
|
||||
{
|
||||
print_employee(employee);
|
||||
}
|
||||
|
|
|
@ -5,10 +5,9 @@
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
//[example_query_sync
|
||||
//[example_text_queries
|
||||
|
||||
#include <boost/mysql.hpp>
|
||||
#include <boost/mysql/handshake_params.hpp>
|
||||
#include <boost/mysql/row_view.hpp>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ssl/context.hpp>
|
||||
|
@ -31,11 +30,11 @@
|
|||
* Indexing a row_view yields a boost::mysql::field_view, which is a variant-like
|
||||
* type representing a single value returned by MySQL.
|
||||
*/
|
||||
void print_employee(const boost::mysql::row& employee)
|
||||
void print_employee(boost::mysql::row_view employee)
|
||||
{
|
||||
std::cout << "Employee '" << employee[0] << " " // first_name (type string)
|
||||
<< employee[1] << "' earns " // last_name (type string)
|
||||
<< employee[2] << " dollars yearly\n"; // salary (type double)
|
||||
std::cout << "Employee '" << employee.at(0) << " " // first_name (string)
|
||||
<< employee.at(1) << "' earns " // last_name (string)
|
||||
<< employee.at(2) << " dollars yearly\n"; // salary (double)
|
||||
}
|
||||
|
||||
void main_impl(int argc, char** argv)
|
||||
|
@ -78,35 +77,24 @@ void main_impl(int argc, char** argv)
|
|||
|
||||
/**
|
||||
* Before using the connection, we have to connect to the server by:
|
||||
* - Establishing the TCP-level session.
|
||||
* - Authenticating to the MySQL server. The SSL handshake is performed as part of this.
|
||||
* connection::connect takes care of both.
|
||||
* - Establishing the TCP-level session.
|
||||
* - Authenticating to the MySQL server. The SSL handshake is performed as part of this.
|
||||
* connection::connect takes care of both.
|
||||
*/
|
||||
conn.connect(*endpoints.begin(), params);
|
||||
|
||||
/**
|
||||
* To issue a SQL query to the database server, use tcp_ssl_connection::query, which takes
|
||||
* the SQL to be executed as parameter and returns a resultset object by lvalue reference.
|
||||
*
|
||||
* Resultset objects represent the result of a query.
|
||||
* They hold metadata describing the fields the resultset holds (in this case, first_name,
|
||||
* last_name and salary). Resultsets don't contain the actual data, but have methods to read it.
|
||||
*
|
||||
* Resultset objects contain the retrieved rows, among other info.
|
||||
* We will get all employees working for 'High Growth Startup'.
|
||||
*/
|
||||
const char* sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
|
||||
boost::mysql::tcp_ssl_resultset result;
|
||||
boost::mysql::resultset result;
|
||||
conn.query(sql, result);
|
||||
|
||||
/**
|
||||
* We will use resultset::read_all(), which will read all our rows into
|
||||
* a boost::mysql::rows object. This is a matrix-like object, specialized for
|
||||
* MySQL fields. Indexing a rows object returns a row_view, which represents
|
||||
* an individual row.
|
||||
*/
|
||||
boost::mysql::rows all_rows;
|
||||
result.read_all(all_rows);
|
||||
for (boost::mysql::row_view employee : all_rows)
|
||||
// We can access the rows using resultset::rows
|
||||
for (boost::mysql::row_view employee : result.rows())
|
||||
{
|
||||
print_employee(employee);
|
||||
}
|
||||
|
@ -115,16 +103,12 @@ void main_impl(int argc, char** argv)
|
|||
// resultset will have no fields and no rows
|
||||
sql = "UPDATE employee SET salary = 10000 WHERE first_name = 'Underpaid'";
|
||||
conn.query(sql, result);
|
||||
ASSERT(
|
||||
result.meta().size() == 0
|
||||
); // meta() returns a collection containing metadata about the query fields
|
||||
ASSERT(result.rows().empty()); // UPDATEs don't retrieve rows
|
||||
|
||||
// Check we have updated our poor intern salary
|
||||
conn.query("SELECT salary FROM employee WHERE first_name = 'Underpaid'", result);
|
||||
result.read_all(all_rows);
|
||||
ASSERT(all_rows.size() == 1);
|
||||
double salary = all_rows[0][0].as_double();
|
||||
ASSERT(salary == 10000);
|
||||
double salary = result.rows().at(0).at(0).as_double();
|
||||
ASSERT(salary == 10000.0);
|
||||
|
||||
// Close the connection. This notifies the MySQL we want to log out
|
||||
// and then closes the underlying socket. This operation implies a network
|
|
@ -8,8 +8,6 @@
|
|||
//[example_timeouts
|
||||
|
||||
#include <boost/mysql.hpp>
|
||||
#include <boost/mysql/handshake_params.hpp>
|
||||
#include <boost/mysql/tcp_ssl.hpp>
|
||||
|
||||
#include <boost/asio/awaitable.hpp>
|
||||
#include <boost/asio/co_spawn.hpp>
|
||||
|
@ -37,9 +35,9 @@ constexpr std::chrono::milliseconds TIMEOUT(2000);
|
|||
|
||||
void print_employee(boost::mysql::row_view employee)
|
||||
{
|
||||
std::cout << "Employee '" << employee[0] << " " // first_name (string)
|
||||
<< employee[1] << "' earns " // last_name (string)
|
||||
<< employee[2] << " dollars yearly\n"; // salary (double)
|
||||
std::cout << "Employee '" << employee.at(0) << " " // first_name (string)
|
||||
<< employee.at(1) << "' earns " // last_name (string)
|
||||
<< employee.at(2) << " dollars yearly\n"; // salary (double)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -81,7 +79,8 @@ boost::asio::awaitable<void> start_query(
|
|||
boost::asio::ip::tcp::resolver& resolver,
|
||||
boost::asio::steady_timer& timer,
|
||||
const boost::mysql::handshake_params& params,
|
||||
const char* hostname
|
||||
const char* hostname,
|
||||
const char* company_id
|
||||
)
|
||||
{
|
||||
try
|
||||
|
@ -100,26 +99,30 @@ boost::asio::awaitable<void> start_query(
|
|||
conn.async_connect(*endpoints.begin(), params, use_awaitable)
|
||||
));
|
||||
|
||||
// Issue the query to the server
|
||||
const char*
|
||||
sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
|
||||
boost::mysql::tcp_ssl_resultset result;
|
||||
timer.expires_after(TIMEOUT);
|
||||
// We will be using company_id, which is untrusted user input, so we will use a prepared
|
||||
// statement.
|
||||
boost::mysql::tcp_ssl_statement stmt;
|
||||
check_timeout(co_await (
|
||||
timer.async_wait(use_awaitable) || conn.async_query(sql, result, use_awaitable)
|
||||
timer.async_wait(use_awaitable) ||
|
||||
conn.async_prepare_statement(
|
||||
"SELECT first_name, last_name, salary FROM employee WHERE company_id = ?",
|
||||
stmt,
|
||||
use_awaitable
|
||||
)
|
||||
));
|
||||
|
||||
// Read all rows
|
||||
boost::mysql::row row;
|
||||
while (true)
|
||||
// Execute the statement
|
||||
boost::mysql::resultset result;
|
||||
timer.expires_after(TIMEOUT);
|
||||
check_timeout(co_await (
|
||||
timer.async_wait(use_awaitable) ||
|
||||
stmt.async_execute(std::make_tuple(company_id), result, use_awaitable)
|
||||
));
|
||||
|
||||
// Print all the obtained rows
|
||||
for (boost::mysql::row_view employee : result.rows())
|
||||
{
|
||||
timer.expires_after(TIMEOUT);
|
||||
check_timeout(co_await (
|
||||
timer.async_wait(use_awaitable) || result.async_read_one(row, use_awaitable)
|
||||
));
|
||||
if (result.complete())
|
||||
break;
|
||||
print_employee(row);
|
||||
print_employee(employee);
|
||||
}
|
||||
|
||||
// Notify the MySQL server we want to quit, then close the underlying connection.
|
||||
|
@ -138,14 +141,19 @@ boost::asio::awaitable<void> start_query(
|
|||
|
||||
void main_impl(int argc, char** argv)
|
||||
{
|
||||
if (argc != 4)
|
||||
if (argc != 4 && argc != 5)
|
||||
{
|
||||
std::cerr << "Usage: " << argv[0] << " <username> <password> <server-hostname>\n";
|
||||
std::cerr << "Usage: " << argv[0]
|
||||
<< " <username> <password> <server-hostname> [company-id]\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const char* hostname = argv[3];
|
||||
|
||||
// The company_id whose employees we will be listing. This
|
||||
// is user-supplied input, and should be treated as untrusted.
|
||||
const char* company_id = argc == 5 ? argv[4] : "HGS";
|
||||
|
||||
// I/O context and connection. We use SSL because MySQL 8+ default settings require it.
|
||||
boost::asio::io_context ctx;
|
||||
boost::asio::ssl::context ssl_ctx(boost::asio::ssl::context::tls_client);
|
||||
|
@ -162,14 +170,11 @@ void main_impl(int argc, char** argv)
|
|||
// Resolver for hostname resolution
|
||||
boost::asio::ip::tcp::resolver resolver(ctx.get_executor());
|
||||
|
||||
/**
|
||||
* The entry point. We pass in a function returning
|
||||
* a boost::asio::awaitable<void>, as required.
|
||||
*/
|
||||
// The entry point. We pass in a function returning a boost::asio::awaitable<void>, as required.
|
||||
boost::asio::co_spawn(
|
||||
ctx.get_executor(),
|
||||
[&conn, &resolver, &timer, params, hostname] {
|
||||
return start_query(conn, resolver, timer, params, hostname);
|
||||
[&conn, &resolver, &timer, params, hostname, company_id] {
|
||||
return start_query(conn, resolver, timer, params, hostname, company_id);
|
||||
},
|
||||
boost::asio::detached
|
||||
);
|
||||
|
|
|
@ -62,22 +62,17 @@ void main_impl(int argc, char** argv)
|
|||
//[tutorial_query
|
||||
// Issue the SQL query to the server
|
||||
const char* sql = "SELECT \"Hello world!\"";
|
||||
boost::mysql::tcp_ssl_resultset result;
|
||||
boost::mysql::resultset result;
|
||||
conn.query(sql, result);
|
||||
//]
|
||||
|
||||
//[tutorial_read
|
||||
// Read the query results into memory
|
||||
boost::mysql::rows all_rows;
|
||||
result.read_all(all_rows);
|
||||
//]
|
||||
|
||||
//[tutorial_fields
|
||||
//[tutorial_resultset
|
||||
// Print the first field in the first row
|
||||
std::cout << all_rows.at(0).at(0) << std::endl;
|
||||
std::cout << result.rows().at(0).at(0) << std::endl;
|
||||
//]
|
||||
|
||||
//[tutorial_close
|
||||
// Close the connection
|
||||
conn.close();
|
||||
//]
|
||||
}
|
||||
|
|
|
@ -14,12 +14,13 @@
|
|||
#include <boost/system/system_error.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <tuple>
|
||||
|
||||
void print_employee(boost::mysql::row_view employee)
|
||||
{
|
||||
std::cout << "Employee '" << employee[0] << " " // first_name (string)
|
||||
<< employee[1] << "' earns " // last_name (string)
|
||||
<< employee[2] << " dollars yearly\n"; // salary (double)
|
||||
std::cout << "Employee '" << employee.at(0) << " " // first_name (string)
|
||||
<< employee.at(1) << "' earns " // last_name (string)
|
||||
<< employee.at(2) << " dollars yearly\n"; // salary (double)
|
||||
}
|
||||
|
||||
#define ASSERT(expr) \
|
||||
|
@ -29,7 +30,8 @@ void print_employee(boost::mysql::row_view employee)
|
|||
exit(1); \
|
||||
}
|
||||
|
||||
/* UNIX sockets are only available in, er, UNIX systems. Typedefs for
|
||||
/**
|
||||
* UNIX sockets are only available on, er, UNIX systems. Typedefs for
|
||||
* UNIX socket-based connections are only available in UNIX systems.
|
||||
* Check for BOOST_ASIO_HAS_LOCAL_SOCKETS to know if UNIX socket
|
||||
* typedefs are available in your system.
|
||||
|
@ -40,15 +42,16 @@ void main_impl(int argc, char** argv)
|
|||
{
|
||||
if (argc != 3 && argc != 4)
|
||||
{
|
||||
std::cerr << "Usage: " << argv[0] << " <username> <password> [<socket-path>]\n";
|
||||
std::cerr << "Usage: " << argv[0]
|
||||
<< " <username> <password> [<socket-path>] [<company-id>]\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const char* socket_path = "/var/run/mysqld/mysqld.sock";
|
||||
if (argc == 4)
|
||||
{
|
||||
socket_path = argv[3];
|
||||
}
|
||||
const char* socket_path = argc >= 4 ? argv[3] : "/var/run/mysqld/mysqld.sock";
|
||||
|
||||
// The company_id whose employees we will be listing. This
|
||||
// is user-supplied input, and should be treated as untrusted.
|
||||
const char* company_id = argc == 5 ? argv[4] : "HGS";
|
||||
|
||||
/**
|
||||
* Connection parameters that tell us where and how to connect to the MySQL server.
|
||||
|
@ -71,30 +74,24 @@ void main_impl(int argc, char** argv)
|
|||
boost::mysql::unix_ssl_connection conn(ctx, ssl_ctx);
|
||||
conn.connect(ep, params); // UNIX socket connect and MySQL handshake
|
||||
|
||||
const char* sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
|
||||
boost::mysql::unix_ssl_resultset result;
|
||||
conn.query(sql, result);
|
||||
// We will be using company_id, which is untrusted user input, so we will use a prepared
|
||||
// statement.
|
||||
boost::mysql::unix_ssl_statement stmt;
|
||||
conn.prepare_statement(
|
||||
"SELECT first_name, last_name, salary FROM employee WHERE company_id = ?",
|
||||
stmt
|
||||
);
|
||||
|
||||
// Get all the rows in the resultset
|
||||
boost::mysql::rows all_rows;
|
||||
result.read_all(all_rows);
|
||||
for (const auto& employee : all_rows)
|
||||
// Execute the statement
|
||||
boost::mysql::resultset result;
|
||||
stmt.execute(std::make_tuple(company_id), result);
|
||||
|
||||
// Print employees
|
||||
for (boost::mysql::row_view employee : result.rows())
|
||||
{
|
||||
print_employee(employee);
|
||||
}
|
||||
|
||||
// We can issue any SQL statement, not only SELECTs. In this case, the returned
|
||||
// resultset will have no fields and no rows
|
||||
sql = "UPDATE employee SET salary = 10000 WHERE first_name = 'Underpaid'";
|
||||
conn.query(sql, result);
|
||||
ASSERT(result.complete()); // UPDATE queries never return rows
|
||||
|
||||
// Check we have updated our poor intern salary
|
||||
conn.query("SELECT salary FROM employee WHERE first_name = 'Underpaid'", result);
|
||||
result.read_all(all_rows);
|
||||
double salary = all_rows.at(0).at(0).as_double();
|
||||
ASSERT(salary == 10000);
|
||||
|
||||
// Notify the MySQL server we want to quit, then close the underlying connection.
|
||||
conn.close();
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ public:
|
|||
/**
|
||||
* \brief Initializing constructor.
|
||||
* \param init_read_buffer_size Initial size of the read buffer. A bigger read buffer
|
||||
* can increase the number of rows returned by \ref resultset::read_some.
|
||||
* can increase the number of rows returned by \ref connection::read_some_rows.
|
||||
*/
|
||||
constexpr buffer_params(std::size_t init_read_buffer_size = 0) noexcept
|
||||
: initial_read_buffer_size_(init_read_buffer_size)
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace mysql {
|
|||
* \brief Represents the database type of a MySQL column.
|
||||
* \details This represents a database type, as opposed to \ref field_kind, which represents a
|
||||
* C++ type.
|
||||
*
|
||||
*\n
|
||||
* Unless otherwise noted, the names in this enumeration
|
||||
* directly correspond to the names of the types you would use in
|
||||
* a `CREATE TABLE` statement to create a column of this type
|
||||
|
@ -53,7 +53,6 @@ enum class column_type
|
|||
};
|
||||
|
||||
/**
|
||||
* \relates column_type
|
||||
* \brief Streams a `column_type`.
|
||||
*/
|
||||
inline std::ostream& operator<<(std::ostream& os, column_type t)
|
||||
|
|
|
@ -8,13 +8,19 @@
|
|||
#ifndef BOOST_MYSQL_CONNECTION_HPP
|
||||
#define BOOST_MYSQL_CONNECTION_HPP
|
||||
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
#include <boost/mysql/detail/protocol/protocol_types.hpp>
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
#include <boost/mysql/handshake_params.hpp>
|
||||
#include <boost/mysql/resultset.hpp>
|
||||
#include <boost/mysql/row.hpp>
|
||||
#include <boost/mysql/row_view.hpp>
|
||||
#include <boost/mysql/rows.hpp>
|
||||
#include <boost/mysql/rows_view.hpp>
|
||||
#include <boost/mysql/statement.hpp>
|
||||
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
#include <boost/mysql/detail/protocol/protocol_types.hpp>
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
|
@ -33,48 +39,33 @@ namespace mysql {
|
|||
* `connection` is the main I/O object that this library implements. It owns a `Stream` object that
|
||||
* is accessed by functions involving network operations, as well as session state. You can access
|
||||
* the stream using \ref connection::stream, and its executor via \ref connection::get_executor. The
|
||||
* executor used by this object is always the same as the underlying stream. Other I/O objects
|
||||
* (`statement` and `resultset`) are proxy I/O objects, which means that they pointing to the stream
|
||||
* and state owned by `*this`.
|
||||
*\n
|
||||
* `connection` is move constructible and move assignable, but not copyable.
|
||||
* Moved-from connection objects are left in a state that makes them not
|
||||
* usable for most of the operations. The function \ref connection::valid
|
||||
* returns whether an object is in a usable state or not. The only allowed
|
||||
* operations on moved-from connections are:
|
||||
*\n
|
||||
* * Destroying them.
|
||||
* * Participating in other move construction/assignment operations.
|
||||
* * Calling \ref connection::valid.
|
||||
*\n
|
||||
* In particular, it is __not__ allowed to call \ref connection::handshake
|
||||
* on a moved-from connection in order to re-open it.
|
||||
* executor used by this object is always the same as the underlying stream.
|
||||
*/
|
||||
template <class Stream>
|
||||
class connection
|
||||
{
|
||||
std::unique_ptr<detail::channel<Stream>> channel_;
|
||||
|
||||
detail::channel<Stream>& get_channel() noexcept
|
||||
{
|
||||
assert(valid());
|
||||
return *channel_;
|
||||
}
|
||||
const detail::channel<Stream>& get_channel() const noexcept
|
||||
{
|
||||
assert(valid());
|
||||
assert(channel_ != nullptr);
|
||||
return *channel_;
|
||||
}
|
||||
error_info& shared_info() noexcept { return get_channel().shared_info(); }
|
||||
|
||||
public:
|
||||
// TODO: hide this
|
||||
detail::channel<Stream>& get_channel() noexcept
|
||||
{
|
||||
assert(channel_ != nullptr);
|
||||
return *channel_;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Initializing constructor.
|
||||
* \details
|
||||
* As part of the initialization, a `Stream` object is created
|
||||
* by forwarding any passed in arguments to its constructor.
|
||||
*
|
||||
* `this->valid()` will return `true` for the newly constructed object.
|
||||
*/
|
||||
template <
|
||||
class... Args,
|
||||
|
@ -87,15 +78,14 @@ public:
|
|||
|
||||
/**
|
||||
* \brief Move constructor.
|
||||
* \details \ref resultset and \ref statement objects referencing `other` will remain valid.
|
||||
* \details \ref statement objects referencing `other` remain usable for I/O operations.
|
||||
*/
|
||||
connection(connection&& other) = default;
|
||||
|
||||
/**
|
||||
* \brief Move assignment.
|
||||
* \details \ref resultset and \ref statement objects referencing `other` will remain valid.
|
||||
* Objects referencing `*this` will no longer be valid. They can be re-used
|
||||
* in I/O object generting operations like \ref query or \ref prepare_statement.
|
||||
* \details \ref statement objects referencing `other` remain usable for I/O operations.
|
||||
* Statements referencing `*this` will no longer be usable.
|
||||
*/
|
||||
connection& operator=(connection&& rhs) = default;
|
||||
|
||||
|
@ -104,14 +94,6 @@ public:
|
|||
connection& operator=(const connection&) = delete;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Returns `true` if the object is in a valid state.
|
||||
* \details This function always returns `true` except for moved-from
|
||||
* connections. Being `valid()` is a precondition for all network
|
||||
* operations in this class.
|
||||
*/
|
||||
bool valid() const noexcept { return channel_ != nullptr; }
|
||||
|
||||
/// The executor type associated to this object.
|
||||
using executor_type = typename Stream::executor_type;
|
||||
|
||||
|
@ -130,9 +112,6 @@ public:
|
|||
/// The type of prepared statements that can be used with this connection type.
|
||||
using statement_type = statement<Stream>;
|
||||
|
||||
/// The type of resultsets that can be used with this connection type.
|
||||
using resultset_type = resultset<Stream>;
|
||||
|
||||
/**
|
||||
* \brief Returns whether the connection uses SSL or not.
|
||||
* \details This function always returns `false` if the underlying
|
||||
|
@ -214,7 +193,7 @@ public:
|
|||
/**
|
||||
* \brief Performs the MySQL-level handshake.
|
||||
* \details Does not connect the underlying stream.
|
||||
* If the `Stream` template parameter fulfills the __SocketConnection__
|
||||
* If the `Stream` template parameter fulfills the `SocketConnection`
|
||||
* requirements, use \ref connection::connect instead of this function.
|
||||
*\n
|
||||
* If using a SSL-capable stream, the SSL handshake will be performed by this function.
|
||||
|
@ -257,23 +236,18 @@ public:
|
|||
|
||||
/**
|
||||
* \brief Executes a SQL text query.
|
||||
* \details Starts a multi-function operation. This function will write the query request to the
|
||||
* server and read the initial server response, but won't read the generated rows, if any. After
|
||||
* this operation completes, `result` will have \ref resultset::meta populated, and may become
|
||||
* \ref resultset::complete, if the operation did not generate any rows (e.g. it was an
|
||||
* `UPDATE`). `result` will reference `*this`, and will be usable for server interaction as long
|
||||
* as I/O object references to `*this` are valid.
|
||||
* \details
|
||||
* Sends `query_string` to the server for execution and reads the response into `result`.
|
||||
* `query_string` should be encoded using the connection's character set.
|
||||
*\n
|
||||
* If the operation generated any rows, these __must__ be read (by using any of the
|
||||
* `resultset::read_xxx` functions) before engaging in any further operation involving server
|
||||
* communication. Otherwise, the results are undefined.
|
||||
* After this operation completes successfully, `result.has_value() == true`.
|
||||
*\n
|
||||
* This operation involves both reads and writes on the underlying stream.
|
||||
*/
|
||||
void query(boost::string_view query_string, resultset<Stream>& result, error_code&, error_info&);
|
||||
void query(boost::string_view query_string, resultset& result, error_code&, error_info&);
|
||||
|
||||
/// \copydoc query
|
||||
void query(boost::string_view query_string, resultset<Stream>& result);
|
||||
void query(boost::string_view query_string, resultset& result);
|
||||
|
||||
/**
|
||||
* \copydoc query
|
||||
|
@ -281,7 +255,7 @@ public:
|
|||
* If `CompletionToken` is a deferred completion token (e.g. `use_awaitable`), the string
|
||||
* pointed to by `query_string` __must be kept alive by the caller until the operation is
|
||||
* initiated__.
|
||||
*
|
||||
*\n
|
||||
* The handler signature for this operation is `void(boost::mysql::error_code)`.
|
||||
*/
|
||||
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code))
|
||||
|
@ -289,7 +263,7 @@ public:
|
|||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_query(
|
||||
boost::string_view query_string,
|
||||
resultset<Stream>& result,
|
||||
resultset& result,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
)
|
||||
{
|
||||
|
@ -307,7 +281,64 @@ public:
|
|||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_query(
|
||||
boost::string_view query_string,
|
||||
resultset<Stream>& result,
|
||||
resultset& result,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
);
|
||||
|
||||
/**
|
||||
* \brief Starts a text query as a multi-function operation.
|
||||
* \details Writes the query request and reads the initial server response and the column
|
||||
* metadata, but not the generated rows, if any. After this operation completes, `st` will have
|
||||
* \ref execution_state::meta populated, and may become \ref execution_state::complete
|
||||
* if the operation did not generate any rows (e.g. it was an `UPDATE`).
|
||||
*\n
|
||||
* If the operation generated any rows, these <b>must</b> be read (by using \ref read_one_row or
|
||||
* \ref read_some_rows) before engaging in any further operation involving network reads.
|
||||
* Otherwise, the results are undefined.
|
||||
*\n
|
||||
* This operation involves both reads and writes on the underlying stream.
|
||||
*\n
|
||||
* `query_string` should be encoded using the connection's character set.
|
||||
*/
|
||||
void start_query(boost::string_view query_string, execution_state& st, error_code&, error_info&);
|
||||
|
||||
/// \copydoc start_query
|
||||
void start_query(boost::string_view query_string, execution_state& st);
|
||||
|
||||
/**
|
||||
* \copydoc start_query
|
||||
* \details
|
||||
* If `CompletionToken` is a deferred completion token (e.g. `use_awaitable`), the string
|
||||
* pointed to by `query_string` <b>must be kept alive by the caller until the operation is
|
||||
* initiated</b>.
|
||||
*
|
||||
* The handler signature for this operation is `void(boost::mysql::error_code)`.
|
||||
*/
|
||||
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code))
|
||||
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_start_query(
|
||||
boost::string_view query_string,
|
||||
execution_state& st,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
)
|
||||
{
|
||||
return async_start_query(
|
||||
query_string,
|
||||
st,
|
||||
shared_info(),
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
}
|
||||
|
||||
/// \copydoc async_start_query
|
||||
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code))
|
||||
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_start_query(
|
||||
boost::string_view query_string,
|
||||
execution_state& st,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
);
|
||||
|
@ -315,10 +346,11 @@ public:
|
|||
/**
|
||||
* \brief Prepares a statement server-side.
|
||||
* \details
|
||||
* After this operation completes, `result` will reference `*this`. It will be usable for server
|
||||
* interaction as long as I/O object references to `*this` are valid.
|
||||
* After this operation completes, `result` will reference `*this`.
|
||||
*\n
|
||||
* This operation involves both reads and writes on the underlying stream.
|
||||
*\n
|
||||
* `stmt` should be encoded using the connection's character set.
|
||||
*/
|
||||
void prepare_statement(boost::string_view stmt, statement<Stream>& result, error_code&, error_info&);
|
||||
|
||||
|
@ -329,8 +361,8 @@ public:
|
|||
* \copydoc prepare_statement
|
||||
* \details
|
||||
* If `CompletionToken` is a deferred completion token (e.g. `use_awaitable`), the string
|
||||
* pointed to by `stmt` __must be kept alive by the caller until the operation is
|
||||
* initiated__.
|
||||
* pointed to by `stmt` <b>must be kept alive by the caller until the operation is
|
||||
* initiated</b>.
|
||||
*\n
|
||||
* The handler signature for this operation is `void(boost::mysql::error_code)`
|
||||
*/
|
||||
|
@ -363,14 +395,118 @@ public:
|
|||
);
|
||||
|
||||
/**
|
||||
* \brief Closes the connection with the server.
|
||||
* \brief Reads a single row.
|
||||
* \details
|
||||
* If a row was read successfully, returns a non-empty \ref row_view.
|
||||
* If there were no more rows to read, returns an empty `row_view`.
|
||||
*\n
|
||||
* The returned view points into memory owned by `*this`. It will be valid until the
|
||||
* underlying stream performs any other read operation or is destroyed.
|
||||
*\n
|
||||
* `st` must have previously been populated by a function starting the multifunction
|
||||
* operation, like \ref start_query or \ref statement::start_execution. Otherwise, the results
|
||||
* are undefined.
|
||||
*/
|
||||
row_view read_one_row(execution_state& st, error_code& err, error_info& info);
|
||||
|
||||
/// \copydoc read_one_row
|
||||
row_view read_one_row(execution_state& st);
|
||||
|
||||
/**
|
||||
* \copydoc read_one_row
|
||||
*
|
||||
* The handler signature for this operation is
|
||||
* `void(boost::mysql::error_code, boost::mysql::row_view)`.
|
||||
*/
|
||||
template <
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, ::boost::mysql::row_view))
|
||||
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, row_view))
|
||||
async_read_one_row(
|
||||
execution_state& st,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
)
|
||||
{
|
||||
return async_read_one_row(
|
||||
st,
|
||||
get_channel().shared_info(),
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
}
|
||||
|
||||
/// \copydoc async_read_one_row
|
||||
template <
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, ::boost::mysql::row_view))
|
||||
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, row_view))
|
||||
async_read_one_row(
|
||||
execution_state& st,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
);
|
||||
|
||||
/**
|
||||
* \brief Reads a batch of rows.
|
||||
* \details
|
||||
* The number of rows that will be read is unspecified. If the resultset being read
|
||||
* has still rows to read, at least one will be read. If there are no more
|
||||
* rows to be read, returns an empty `rows_view`.
|
||||
* \n
|
||||
* The number of rows that will be read depends on the input buffer size. The bigger the buffer,
|
||||
* the greater the batch size (up to a maximum). You can set the initial buffer size in \ref
|
||||
* connection::connect, using \ref buffer_params::initial_read_buffer_size. The buffer may be
|
||||
* grown bigger by other read operations, if required.
|
||||
* \n
|
||||
* The returned view points into memory owned by `*this`. It will be valid until the
|
||||
* underlying stream performs any other read operation or is destroyed.
|
||||
*/
|
||||
rows_view read_some_rows(execution_state& st, error_code& err, error_info& info);
|
||||
|
||||
/// \copydoc read_some_rows
|
||||
rows_view read_some_rows(execution_state& st);
|
||||
|
||||
/**
|
||||
* \copydoc read_some_rows
|
||||
* \details
|
||||
* The handler signature for this operation is
|
||||
* `void(boost::mysql::error_code, boost::mysql::rows_view)`.
|
||||
*/
|
||||
template <
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, ::boost::mysql::rows_view))
|
||||
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, rows_view))
|
||||
async_read_some_rows(
|
||||
execution_state& st,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
)
|
||||
{
|
||||
return async_read_some_rows(
|
||||
st,
|
||||
get_channel().shared_info(),
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
}
|
||||
|
||||
/// \copydoc async_read_some_rows
|
||||
template <
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, ::boost::mysql::rows_view))
|
||||
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, rows_view))
|
||||
async_read_some_rows(
|
||||
execution_state& st,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
);
|
||||
|
||||
/**
|
||||
* \brief Closes the connection to the server.
|
||||
* \details
|
||||
* This function is only available if `Stream` satisfies the `SocketStream` concept.
|
||||
*\n
|
||||
* Sends a quit request, performs the TLS shutdown (if required)
|
||||
* and closes the underlying stream. Prefer this function to \ref connection::quit.
|
||||
*\n
|
||||
* After calling this function, any \ref statement and \ref resultset referencing `*this` will
|
||||
* After calling this function, any \ref statement referencing `*this` will
|
||||
* no longer be usable for server interaction.
|
||||
*\n
|
||||
*/
|
||||
|
@ -407,7 +543,10 @@ public:
|
|||
* this function will also perform the SSL shutdown. You should
|
||||
* close the underlying physical connection after calling this function.
|
||||
*\n
|
||||
* If the `Stream` template parameter fulfills the __SocketConnection__
|
||||
* After calling this function, any \ref statement referencing `*this` will
|
||||
* no longer be usable for server interaction.
|
||||
*\n
|
||||
* If the `Stream` template parameter fulfills the `SocketConnection`
|
||||
* requirements, use \ref connection::close instead of this function,
|
||||
* as it also takes care of closing the underlying stream.
|
||||
*/
|
||||
|
|
|
@ -8,9 +8,10 @@
|
|||
#ifndef BOOST_MYSQL_DETAIL_AUXILIAR_FIELD_TYPE_TRAITS_HPP
|
||||
#define BOOST_MYSQL_DETAIL_AUXILIAR_FIELD_TYPE_TRAITS_HPP
|
||||
|
||||
#include <boost/mysql/field_view.hpp>
|
||||
|
||||
#include <boost/mysql/detail/auxiliar/void_t.hpp>
|
||||
#include <boost/mysql/detail/config.hpp>
|
||||
#include <boost/mysql/field_view.hpp>
|
||||
|
||||
#include <boost/mp11/algorithm.hpp>
|
||||
#include <boost/mp11/list.hpp>
|
||||
|
|
|
@ -8,9 +8,10 @@
|
|||
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_CLOSE_CONNECTION_HPP
|
||||
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_CLOSE_CONNECTION_HPP
|
||||
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
#include <boost/mysql/error.hpp>
|
||||
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
@ -18,7 +19,9 @@ namespace detail {
|
|||
template <class SocketStream>
|
||||
void close_connection(channel<SocketStream>& chan, error_code& code, error_info& info);
|
||||
|
||||
template <class SocketStream, class CompletionToken>
|
||||
template <
|
||||
class SocketStream,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_close_connection(channel<SocketStream>& chan, CompletionToken&& token, error_info& info);
|
||||
|
||||
|
|
|
@ -8,10 +8,11 @@
|
|||
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_CLOSE_STATEMENT_HPP
|
||||
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_CLOSE_STATEMENT_HPP
|
||||
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/statement_base.hpp>
|
||||
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
@ -24,7 +25,9 @@ void close_statement(
|
|||
error_info& info
|
||||
);
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_close_statement(
|
||||
channel<Stream>& chan,
|
||||
|
|
|
@ -8,10 +8,11 @@
|
|||
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_CONNECT_HPP
|
||||
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_CONNECT_HPP
|
||||
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/handshake_params.hpp>
|
||||
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
@ -25,7 +26,9 @@ void connect(
|
|||
error_info& info
|
||||
);
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_connect(
|
||||
channel<Stream>& chan,
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_EXECUTE_GENERIC_HPP
|
||||
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_EXECUTE_GENERIC_HPP
|
||||
|
||||
#include <boost/mysql/detail/auxiliar/field_type_traits.hpp>
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
#include <boost/mysql/detail/config.hpp>
|
||||
#include <boost/mysql/detail/protocol/prepared_statement_messages.hpp>
|
||||
#include <boost/mysql/detail/protocol/query_messages.hpp>
|
||||
#include <boost/mysql/detail/protocol/resultset_encoding.hpp>
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/resultset_base.hpp>
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#ifdef BOOST_MYSQL_HAS_CONCEPTS
|
||||
#include <concepts>
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
||||
#ifdef BOOST_MYSQL_HAS_CONCEPTS
|
||||
// clang-format off
|
||||
template <class T>
|
||||
struct is_com_stmt_execute_packet : std::false_type {};
|
||||
|
||||
template <class FieldViewFwdIterator>
|
||||
struct is_com_stmt_execute_packet<com_stmt_execute_packet<FieldViewFwdIterator>> : std::true_type {};
|
||||
|
||||
template <class T>
|
||||
concept execute_request = std::is_same_v<T, com_query_packet> || is_com_stmt_execute_packet<T>::value;
|
||||
|
||||
template <class T>
|
||||
concept execute_request_maker = requires(const T& t)
|
||||
{
|
||||
typename T::storage_type; // Used by prepared statements (tuple version) to store field_view's
|
||||
requires std::default_initializable<typename T::storage_type>;
|
||||
requires std::copy_constructible<T>;
|
||||
{ t.make_storage() } -> std::same_as<typename T::storage_type>;
|
||||
{ t.make_request(std::declval<const typename T::storage_type&>()) } -> execute_request;
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
#define BOOST_MYSQL_EXECUTE_REQUEST ::boost::mysql::detail::execute_request
|
||||
#define BOOST_MYSQL_EXECUTE_REQUEST_MAKER ::boost::mysql::detail::execute_request_maker
|
||||
|
||||
#else // BOOST_MYSQL_HAS_CONCEPTS
|
||||
|
||||
#define BOOST_MYSQL_EXECUTE_REQUEST class
|
||||
#define BOOST_MYSQL_EXECUTE_REQUEST_MAKER class
|
||||
|
||||
#endif // BOOST_MYSQL_HAS_CONCEPTS
|
||||
|
||||
// The sync version gets passed directlty the request packet to be serialized.
|
||||
// There is no need to defer the serialization here.
|
||||
template <class Stream, BOOST_MYSQL_EXECUTE_REQUEST Request>
|
||||
void execute_generic(
|
||||
resultset_encoding encoding,
|
||||
channel<Stream>& channel,
|
||||
const Request& request,
|
||||
resultset_base& output,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
);
|
||||
|
||||
// The async version gets passed a request maker, holding enough data to create
|
||||
// the request packet when the async op is started. Used by statement execution
|
||||
// with tuple params, to make lifetimes more user-friendly when using deferred tokens.
|
||||
template <class Stream, BOOST_MYSQL_EXECUTE_REQUEST_MAKER RequestMaker, class CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_execute_generic(
|
||||
resultset_encoding encoding,
|
||||
channel<Stream>& chan,
|
||||
RequestMaker&& reqmaker, // this should always by an rvalue
|
||||
resultset_base& output,
|
||||
error_info& info,
|
||||
CompletionToken&& token
|
||||
);
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#include <boost/mysql/detail/network_algorithms/impl/execute_generic.hpp>
|
||||
|
||||
#endif /* INCLUDE_MYSQL_IMPL_NETWORK_ALGORITHMS_READ_RESULTSET_HEAD_HPP_ */
|
|
@ -8,64 +8,39 @@
|
|||
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_EXECUTE_STATEMENT_HPP
|
||||
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_EXECUTE_STATEMENT_HPP
|
||||
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/resultset.hpp>
|
||||
#include <boost/mysql/statement_base.hpp>
|
||||
|
||||
#include <boost/mysql/detail/auxiliar/field_type_traits.hpp>
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/execute_options.hpp>
|
||||
#include <boost/mysql/resultset_base.hpp>
|
||||
#include <boost/mysql/statement_base.hpp>
|
||||
|
||||
#include <boost/asio/async_result.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
||||
template <class Stream, BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator>
|
||||
void execute_statement(
|
||||
channel<Stream>& channel,
|
||||
const statement_base& stmt,
|
||||
FieldViewFwdIterator params_first,
|
||||
FieldViewFwdIterator params_last,
|
||||
const execute_options& opts,
|
||||
resultset_base& output,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
);
|
||||
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator,
|
||||
class CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_execute_statement(
|
||||
channel<Stream>& chan,
|
||||
const statement_base& stmt,
|
||||
FieldViewFwdIterator params_first,
|
||||
FieldViewFwdIterator params_last,
|
||||
const execute_options& opts,
|
||||
resultset_base& output,
|
||||
error_info& info,
|
||||
CompletionToken&& token
|
||||
);
|
||||
|
||||
template <class Stream, BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple>
|
||||
void execute_statement(
|
||||
channel<Stream>& channel,
|
||||
const statement_base& stmt,
|
||||
const FieldLikeTuple& params,
|
||||
const execute_options& opts,
|
||||
resultset_base& output,
|
||||
resultset& output,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
);
|
||||
|
||||
template <class Stream, BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple, class CompletionToken>
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_execute_statement(
|
||||
channel<Stream>& chan,
|
||||
const statement_base& stmt,
|
||||
FieldLikeTuple&& params,
|
||||
const execute_options& opts,
|
||||
resultset_base& output,
|
||||
resultset& output,
|
||||
error_info& info,
|
||||
CompletionToken&& token
|
||||
);
|
||||
|
@ -76,4 +51,4 @@ async_execute_statement(
|
|||
|
||||
#include <boost/mysql/detail/network_algorithms/impl/execute_statement.hpp>
|
||||
|
||||
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_EXECUTE_STATEMENT_HPP_ */
|
||||
#endif
|
||||
|
|
|
@ -8,10 +8,11 @@
|
|||
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_HANDSHAKE_HPP
|
||||
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_HANDSHAKE_HPP
|
||||
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/handshake_params.hpp>
|
||||
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
@ -24,7 +25,9 @@ void handshake(
|
|||
error_info& info
|
||||
);
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_handshake(
|
||||
channel<Stream>& channel,
|
||||
|
|
|
@ -81,7 +81,9 @@ void boost::mysql::detail::close_connection(
|
|||
}
|
||||
}
|
||||
|
||||
template <class SocketStream, class CompletionToken>
|
||||
template <
|
||||
class SocketStream,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
||||
boost::mysql::detail::async_close_connection(
|
||||
channel<SocketStream>& chan,
|
||||
|
|
|
@ -90,7 +90,9 @@ void boost::mysql::detail::
|
|||
stmt.reset();
|
||||
}
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
||||
boost::mysql::detail::async_close_statement(
|
||||
channel<Stream>& chan,
|
||||
|
|
|
@ -92,7 +92,9 @@ void boost::mysql::detail::connect(
|
|||
}
|
||||
}
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
||||
boost::mysql::detail::async_connect(
|
||||
channel<Stream>& chan,
|
||||
|
|
|
@ -10,162 +10,78 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <boost/mysql/detail/auxiliar/stringize.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/execute_generic.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/execute_statement.hpp>
|
||||
#include <boost/mysql/detail/protocol/prepared_statement_messages.hpp>
|
||||
#include <boost/mysql/detail/protocol/resultset_encoding.hpp>
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/field_view.hpp>
|
||||
#include <boost/mysql/resultset_base.hpp>
|
||||
#include <boost/mysql/resultset.hpp>
|
||||
#include <boost/mysql/statement_base.hpp>
|
||||
|
||||
#include <boost/asio/compose.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/mp11/integer_sequence.hpp>
|
||||
#include <boost/mysql/detail/auxiliar/field_type_traits.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/execute_statement.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/read_all_rows.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/start_statement_execution.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
||||
template <BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator>
|
||||
com_stmt_execute_packet<FieldViewFwdIterator> make_stmt_execute_packet(
|
||||
std::uint32_t stmt_id,
|
||||
FieldViewFwdIterator params_first,
|
||||
FieldViewFwdIterator params_last
|
||||
) noexcept(std::is_nothrow_copy_constructible<FieldViewFwdIterator>::value)
|
||||
template <class Stream, BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple>
|
||||
struct execute_statement_op : boost::asio::coroutine
|
||||
{
|
||||
return com_stmt_execute_packet<FieldViewFwdIterator>{
|
||||
stmt_id,
|
||||
std::uint8_t(0), // flags
|
||||
std::uint32_t(1), // iteration count
|
||||
std::uint8_t(1), // new params flag: set
|
||||
params_first,
|
||||
params_last,
|
||||
};
|
||||
}
|
||||
|
||||
template <BOOST_MYSQL_FIELD_LIKE... T, std::size_t... I>
|
||||
std::array<field_view, sizeof...(T)> tuple_to_array_impl(const std::tuple<T...>& t, boost::mp11::index_sequence<I...>) noexcept
|
||||
{
|
||||
return std::array<field_view, sizeof...(T)>{{field_view(std::get<I>(t))...}};
|
||||
}
|
||||
|
||||
template <BOOST_MYSQL_FIELD_LIKE... T>
|
||||
std::array<field_view, sizeof...(T)> tuple_to_array(const std::tuple<T...>& t) noexcept
|
||||
{
|
||||
return tuple_to_array_impl(t, boost::mp11::make_index_sequence<sizeof...(T)>());
|
||||
}
|
||||
|
||||
template <BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator>
|
||||
class stmt_execute_request_maker_it
|
||||
{
|
||||
std::uint32_t stmt_id_;
|
||||
FieldViewFwdIterator first_;
|
||||
FieldViewFwdIterator last_;
|
||||
|
||||
public:
|
||||
struct storage_type
|
||||
{
|
||||
};
|
||||
|
||||
stmt_execute_request_maker_it(
|
||||
std::uint32_t stmt_id,
|
||||
FieldViewFwdIterator first,
|
||||
FieldViewFwdIterator last
|
||||
) noexcept(std::is_nothrow_copy_constructible<FieldViewFwdIterator>::value)
|
||||
: stmt_id_(stmt_id), first_(first), last_(last)
|
||||
{
|
||||
}
|
||||
|
||||
storage_type make_storage() const noexcept { return storage_type(); }
|
||||
|
||||
com_stmt_execute_packet<FieldViewFwdIterator> make_request(storage_type) const
|
||||
noexcept(std::is_nothrow_copy_constructible<FieldViewFwdIterator>::value)
|
||||
{
|
||||
return make_stmt_execute_packet(stmt_id_, first_, last_);
|
||||
}
|
||||
};
|
||||
|
||||
template <BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple>
|
||||
class stmt_execute_request_maker_tuple
|
||||
{
|
||||
std::uint32_t stmt_id_;
|
||||
channel<Stream>& chan_;
|
||||
error_info& output_info_;
|
||||
const statement_base& stmt_;
|
||||
FieldLikeTuple params_;
|
||||
|
||||
public:
|
||||
using storage_type = std::array<field_view, std::tuple_size<FieldLikeTuple>::value>;
|
||||
resultset& output_;
|
||||
|
||||
// We need a deduced context to enable perfect forwarding
|
||||
template <BOOST_MYSQL_FIELD_LIKE_TUPLE DeducedTuple>
|
||||
stmt_execute_request_maker_tuple(std::uint32_t stmt_id, DeducedTuple&& params) noexcept(
|
||||
std::is_nothrow_constructible<
|
||||
FieldLikeTuple,
|
||||
decltype(std::forward<DeducedTuple>(params))>::value
|
||||
)
|
||||
: stmt_id_(stmt_id), params_(std::forward<DeducedTuple>(params))
|
||||
execute_statement_op(
|
||||
channel<Stream>& chan,
|
||||
error_info& output_info,
|
||||
const statement_base& stmt,
|
||||
DeducedTuple&& params,
|
||||
resultset& output
|
||||
) noexcept
|
||||
: chan_(chan),
|
||||
output_info_(output_info),
|
||||
stmt_(stmt),
|
||||
params_(std::forward<DeducedTuple>(params)),
|
||||
output_(output)
|
||||
{
|
||||
}
|
||||
|
||||
storage_type make_storage() const noexcept { return tuple_to_array(params_); }
|
||||
|
||||
com_stmt_execute_packet<const field_view*> make_request(const storage_type& st) const
|
||||
{
|
||||
return make_stmt_execute_packet(stmt_id_, st.data(), st.data() + st.size());
|
||||
}
|
||||
};
|
||||
|
||||
inline error_code check_num_params(
|
||||
const statement_base& stmt,
|
||||
std::size_t param_count,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
if (param_count != stmt.num_params())
|
||||
{
|
||||
info.set_message(detail::stringize(
|
||||
"execute statement: expected ",
|
||||
stmt.num_params(),
|
||||
" params, but got ",
|
||||
param_count
|
||||
));
|
||||
return make_error_code(errc::wrong_num_params);
|
||||
}
|
||||
else
|
||||
{
|
||||
return error_code();
|
||||
}
|
||||
}
|
||||
|
||||
template <BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator>
|
||||
error_code check_num_params(
|
||||
const statement_base& stmt,
|
||||
FieldViewFwdIterator params_first,
|
||||
FieldViewFwdIterator params_last,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
return check_num_params(stmt, std::distance(params_first, params_last), info);
|
||||
}
|
||||
|
||||
struct fast_fail_op : boost::asio::coroutine
|
||||
{
|
||||
error_code err_;
|
||||
|
||||
fast_fail_op(error_code err) noexcept : err_(err) {}
|
||||
|
||||
template <class Self>
|
||||
void operator()(Self& self)
|
||||
void operator()(Self& self, error_code err = {})
|
||||
{
|
||||
// Error checking
|
||||
if (err)
|
||||
{
|
||||
self.complete(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Normal path
|
||||
BOOST_ASIO_CORO_REENTER(*this)
|
||||
{
|
||||
BOOST_ASIO_CORO_YIELD boost::asio::post(std::move(self));
|
||||
self.complete(err_);
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
async_start_statement_execution(
|
||||
chan_,
|
||||
stmt_,
|
||||
std::move(params_),
|
||||
output_.state(),
|
||||
output_info_,
|
||||
std::move(self)
|
||||
);
|
||||
|
||||
BOOST_ASIO_CORO_YIELD async_read_all_rows(
|
||||
chan_,
|
||||
output_.state(),
|
||||
output_.mutable_rows(),
|
||||
output_info_,
|
||||
std::move(self)
|
||||
);
|
||||
|
||||
self.complete(error_code());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -174,124 +90,49 @@ struct fast_fail_op : boost::asio::coroutine
|
|||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
template <class Stream, BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator>
|
||||
void boost::mysql::detail::execute_statement(
|
||||
channel<Stream>& chan,
|
||||
const statement_base& stmt,
|
||||
FieldViewFwdIterator params_first,
|
||||
FieldViewFwdIterator params_last,
|
||||
const execute_options&,
|
||||
resultset_base& output,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
err = check_num_params(stmt, params_first, params_last, info);
|
||||
if (!err)
|
||||
{
|
||||
execute_generic(
|
||||
resultset_encoding::binary,
|
||||
chan,
|
||||
make_stmt_execute_packet(stmt.id(), params_first, params_last),
|
||||
output,
|
||||
err,
|
||||
info
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator,
|
||||
class CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
||||
boost::mysql::detail::async_execute_statement(
|
||||
channel<Stream>& chan,
|
||||
const statement_base& stmt,
|
||||
FieldViewFwdIterator params_first,
|
||||
FieldViewFwdIterator params_last,
|
||||
const execute_options&,
|
||||
resultset_base& output,
|
||||
error_info& info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
error_code err = check_num_params(stmt, params_first, params_last, info);
|
||||
if (err)
|
||||
{
|
||||
return boost::asio::async_compose<CompletionToken, void(error_code)>(
|
||||
fast_fail_op(err),
|
||||
token,
|
||||
chan
|
||||
);
|
||||
}
|
||||
return async_execute_generic(
|
||||
resultset_encoding::binary,
|
||||
chan,
|
||||
stmt_execute_request_maker_it<FieldViewFwdIterator>(stmt.id(), params_first, params_last),
|
||||
output,
|
||||
info,
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
}
|
||||
|
||||
template <class Stream, BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple>
|
||||
void boost::mysql::detail::execute_statement(
|
||||
channel<Stream>& channel,
|
||||
const statement_base& stmt,
|
||||
const FieldLikeTuple& params,
|
||||
const execute_options& opts,
|
||||
resultset_base& output,
|
||||
resultset& output,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
auto params_array = tuple_to_array(params);
|
||||
execute_statement(
|
||||
channel,
|
||||
stmt,
|
||||
params_array.begin(),
|
||||
params_array.end(),
|
||||
opts,
|
||||
output,
|
||||
err,
|
||||
info
|
||||
);
|
||||
start_statement_execution(channel, stmt, params, output.state(), err, info);
|
||||
if (err)
|
||||
return;
|
||||
|
||||
read_all_rows(channel, output.state(), output.mutable_rows(), err, info);
|
||||
}
|
||||
|
||||
template <class Stream, BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple, class CompletionToken>
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
||||
boost::mysql::detail::async_execute_statement(
|
||||
channel<Stream>& chan,
|
||||
const statement_base& stmt,
|
||||
FieldLikeTuple&& params,
|
||||
const execute_options&,
|
||||
resultset_base& output,
|
||||
resultset& output,
|
||||
error_info& info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
using decayed_tuple = typename std::decay<FieldLikeTuple>::type;
|
||||
error_code err = check_num_params(stmt, std::tuple_size<decayed_tuple>::value, info);
|
||||
if (err)
|
||||
{
|
||||
return boost::asio::async_compose<CompletionToken, void(error_code)>(
|
||||
fast_fail_op(err),
|
||||
token,
|
||||
chan
|
||||
);
|
||||
}
|
||||
return async_execute_generic(
|
||||
resultset_encoding::binary,
|
||||
chan,
|
||||
stmt_execute_request_maker_tuple<decayed_tuple>(
|
||||
stmt.id(),
|
||||
std::forward<FieldLikeTuple>(params)
|
||||
return boost::asio::async_compose<CompletionToken, void(boost::mysql::error_code)>(
|
||||
execute_statement_op<Stream, decayed_tuple>(
|
||||
chan,
|
||||
info,
|
||||
stmt,
|
||||
std::forward<FieldLikeTuple>(params),
|
||||
output
|
||||
),
|
||||
output,
|
||||
info,
|
||||
std::forward<CompletionToken>(token)
|
||||
token,
|
||||
chan
|
||||
);
|
||||
}
|
||||
|
||||
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_EXECUTE_STATEMENT_HPP_ */
|
||||
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_EXECUTE_QUERY_HPP_ */
|
||||
|
|
|
@ -452,7 +452,9 @@ void boost::mysql::detail::handshake(
|
|||
channel.set_current_capabilities(processor.negotiated_capabilities());
|
||||
}
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
||||
boost::mysql::detail::async_handshake(
|
||||
channel<Stream>& chan,
|
||||
|
|
|
@ -10,12 +10,13 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/statement_base.hpp>
|
||||
|
||||
#include <boost/mysql/detail/auxiliar/bytestring.hpp>
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/prepare_statement.hpp>
|
||||
#include <boost/mysql/detail/protocol/capabilities.hpp>
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/statement_base.hpp>
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
|
||||
|
@ -217,7 +218,9 @@ void boost::mysql::detail::prepare_statement(
|
|||
}
|
||||
}
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
||||
boost::mysql::detail::async_prepare_statement(
|
||||
channel<Stream>& chan,
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
//
|
||||
// Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_QUERY_HPP
|
||||
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_QUERY_HPP
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/mysql/detail/network_algorithms/query.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/read_all_rows.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/start_query.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
||||
template <class Stream>
|
||||
struct query_op : boost::asio::coroutine
|
||||
{
|
||||
channel<Stream>& chan_;
|
||||
error_info& output_info_;
|
||||
boost::string_view query_;
|
||||
resultset& output_;
|
||||
|
||||
query_op(
|
||||
channel<Stream>& chan,
|
||||
error_info& output_info,
|
||||
boost::string_view q,
|
||||
resultset& output
|
||||
) noexcept
|
||||
: chan_(chan), output_info_(output_info), query_(q), output_(output)
|
||||
{
|
||||
}
|
||||
|
||||
template <class Self>
|
||||
void operator()(Self& self, error_code err = {})
|
||||
{
|
||||
// Error checking
|
||||
if (err)
|
||||
{
|
||||
self.complete(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Normal path
|
||||
BOOST_ASIO_CORO_REENTER(*this)
|
||||
{
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
async_start_query(chan_, query_, output_.state(), output_info_, std::move(self));
|
||||
|
||||
BOOST_ASIO_CORO_YIELD async_read_all_rows(
|
||||
chan_,
|
||||
output_.state(),
|
||||
output_.mutable_rows(),
|
||||
output_info_,
|
||||
std::move(self)
|
||||
);
|
||||
|
||||
self.complete(error_code());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
template <class Stream>
|
||||
void boost::mysql::detail::query(
|
||||
channel<Stream>& channel,
|
||||
boost::string_view query,
|
||||
resultset& output,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
start_query(channel, query, output.state(), err, info);
|
||||
if (err)
|
||||
return;
|
||||
|
||||
read_all_rows(channel, output.state(), output.mutable_rows(), err, info);
|
||||
}
|
||||
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
||||
boost::mysql::detail::async_query(
|
||||
channel<Stream>& chan,
|
||||
boost::string_view query,
|
||||
resultset& output,
|
||||
error_info& info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
return boost::asio::async_compose<CompletionToken, void(boost::mysql::error_code)>(
|
||||
query_op<Stream>(chan, info, query, output),
|
||||
token,
|
||||
chan
|
||||
);
|
||||
}
|
||||
|
||||
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_EXECUTE_QUERY_HPP_ */
|
|
@ -10,9 +10,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <boost/mysql/error.hpp>
|
||||
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/quit_connection.hpp>
|
||||
#include <boost/mysql/error.hpp>
|
||||
|
||||
#include <boost/asio/coroutine.hpp>
|
||||
|
||||
|
@ -85,7 +86,9 @@ void boost::mysql::detail::quit_connection(channel<Stream>& chan, error_code& er
|
|||
}
|
||||
}
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
||||
boost::mysql::detail::async_quit_connection(
|
||||
channel<Stream>& chan,
|
||||
|
|
|
@ -10,11 +10,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
#include <boost/mysql/rows_view.hpp>
|
||||
|
||||
#include <boost/mysql/detail/network_algorithms/read_all_rows.hpp>
|
||||
#include <boost/mysql/detail/protocol/deserialize_row.hpp>
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/resultset_base.hpp>
|
||||
#include <boost/mysql/rows_view.hpp>
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/asio/coroutine.hpp>
|
||||
|
@ -29,8 +30,8 @@ namespace detail {
|
|||
template <class Stream>
|
||||
inline void process_all_rows(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
rows_view& output,
|
||||
execution_state& st,
|
||||
rows& output,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
|
@ -40,7 +41,7 @@ inline void process_all_rows(
|
|||
while (channel.has_read_messages())
|
||||
{
|
||||
// Get the row message
|
||||
auto message = channel.next_read_message(result.sequence_number(), err);
|
||||
auto message = channel.next_read_message(st.sequence_number(), err);
|
||||
if (err)
|
||||
return;
|
||||
|
||||
|
@ -49,7 +50,7 @@ inline void process_all_rows(
|
|||
message,
|
||||
channel.current_capabilities(),
|
||||
channel.buffer_first(),
|
||||
result,
|
||||
st,
|
||||
channel.shared_fields(),
|
||||
err,
|
||||
info
|
||||
|
@ -58,14 +59,14 @@ inline void process_all_rows(
|
|||
return;
|
||||
|
||||
// If we received an EOF, we're done
|
||||
if (result.complete())
|
||||
if (st.complete())
|
||||
{
|
||||
offsets_to_string_views(channel.shared_fields(), channel.buffer_first());
|
||||
output = rows_view(
|
||||
channel.shared_fields().data(),
|
||||
channel.shared_fields().size(),
|
||||
result.meta().size()
|
||||
st.meta().size()
|
||||
);
|
||||
offsets_to_string_views(channel.shared_fields(), channel.buffer_first());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -76,85 +77,21 @@ struct read_all_rows_op : boost::asio::coroutine
|
|||
{
|
||||
channel<Stream>& chan_;
|
||||
error_info& output_info_;
|
||||
resultset_base& resultset_;
|
||||
execution_state& st_;
|
||||
rows& output_;
|
||||
|
||||
read_all_rows_op(
|
||||
channel<Stream>& chan,
|
||||
error_info& output_info,
|
||||
resultset_base& result
|
||||
execution_state& st,
|
||||
rows& output
|
||||
) noexcept
|
||||
: chan_(chan), output_info_(output_info), resultset_(result)
|
||||
: chan_(chan), output_info_(output_info), st_(st), output_(output)
|
||||
{
|
||||
}
|
||||
|
||||
template <class Self>
|
||||
void operator()(Self& self, error_code err = {})
|
||||
{
|
||||
// Error checking
|
||||
if (err)
|
||||
{
|
||||
self.complete(err, rows_view());
|
||||
return;
|
||||
}
|
||||
|
||||
// Normal path
|
||||
rows_view output;
|
||||
BOOST_ASIO_CORO_REENTER(*this)
|
||||
{
|
||||
output_info_.clear();
|
||||
|
||||
// If the resultset_base is already complete, we don't need to read anything
|
||||
if (resultset_.complete())
|
||||
{
|
||||
BOOST_ASIO_CORO_YIELD boost::asio::post(std::move(self));
|
||||
self.complete(error_code(), rows_view());
|
||||
BOOST_ASIO_CORO_YIELD break;
|
||||
}
|
||||
|
||||
// Clear anything from previous runs
|
||||
chan_.shared_fields().clear();
|
||||
|
||||
// Read at least one message
|
||||
while (!resultset_.complete())
|
||||
{
|
||||
// Actually read
|
||||
BOOST_ASIO_CORO_YIELD chan_.async_read_some(std::move(self), true);
|
||||
|
||||
// Process messages
|
||||
process_all_rows(chan_, resultset_, output, err, output_info_);
|
||||
if (err)
|
||||
{
|
||||
self.complete(err, rows_view());
|
||||
BOOST_ASIO_CORO_YIELD break;
|
||||
}
|
||||
}
|
||||
|
||||
// Done
|
||||
self.complete(error_code(), output);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class Stream>
|
||||
struct read_all_rows_op_rows : boost::asio::coroutine
|
||||
{
|
||||
channel<Stream>& chan_;
|
||||
error_info& output_info_;
|
||||
resultset_base& resultset_;
|
||||
rows& output_;
|
||||
|
||||
read_all_rows_op_rows(
|
||||
channel<Stream>& chan,
|
||||
error_info& output_info,
|
||||
resultset_base& result,
|
||||
rows& output
|
||||
) noexcept
|
||||
: chan_(chan), output_info_(output_info), resultset_(result), output_(output)
|
||||
{
|
||||
}
|
||||
|
||||
template <class Self>
|
||||
void operator()(Self& self, error_code err = {}, rows_view rv = {})
|
||||
{
|
||||
// Error checking
|
||||
if (err)
|
||||
|
@ -166,13 +103,36 @@ struct read_all_rows_op_rows : boost::asio::coroutine
|
|||
// Normal path
|
||||
BOOST_ASIO_CORO_REENTER(*this)
|
||||
{
|
||||
// error_info already cleared by child ops
|
||||
output_info_.clear();
|
||||
output_.clear();
|
||||
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
async_read_all_rows(chan_, resultset_, output_info_, std::move(self));
|
||||
// If the resultset_base is already complete, we don't need to read anything
|
||||
if (st_.complete())
|
||||
{
|
||||
BOOST_ASIO_CORO_YIELD boost::asio::post(std::move(self));
|
||||
self.complete(error_code());
|
||||
BOOST_ASIO_CORO_YIELD break;
|
||||
}
|
||||
|
||||
output_ = rv;
|
||||
// Clear anything from previous runs
|
||||
chan_.shared_fields().clear();
|
||||
|
||||
// Read at least one message
|
||||
while (!st_.complete())
|
||||
{
|
||||
// Actually read
|
||||
BOOST_ASIO_CORO_YIELD chan_.async_read_some(std::move(self), true);
|
||||
|
||||
// Process messages
|
||||
process_all_rows(chan_, st_, output_, err, output_info_);
|
||||
if (err)
|
||||
{
|
||||
self.complete(err);
|
||||
BOOST_ASIO_CORO_YIELD break;
|
||||
}
|
||||
}
|
||||
|
||||
// Done
|
||||
self.complete(error_code());
|
||||
}
|
||||
}
|
||||
|
@ -182,83 +142,56 @@ struct read_all_rows_op_rows : boost::asio::coroutine
|
|||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
template <class Stream>
|
||||
boost::mysql::rows_view boost::mysql::detail::read_all_rows(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
// If the resultset_base is already complete, we don't need to read anything
|
||||
if (result.complete())
|
||||
{
|
||||
return rows_view();
|
||||
}
|
||||
|
||||
// Clear anything from previous runs
|
||||
channel.shared_fields().clear();
|
||||
|
||||
rows_view output;
|
||||
while (!result.complete())
|
||||
{
|
||||
// Read from the stream until there is at least one message
|
||||
channel.read_some(err, true);
|
||||
if (err)
|
||||
return rows_view();
|
||||
|
||||
// Process read messages
|
||||
process_all_rows(channel, result, output, err, info);
|
||||
if (err)
|
||||
return rows_view();
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
|
||||
CompletionToken,
|
||||
void(boost::mysql::error_code, boost::mysql::rows_view)
|
||||
)
|
||||
boost::mysql::detail::async_read_all_rows(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
return boost::asio::async_compose<CompletionToken, void(error_code, rows_view)>(
|
||||
read_all_rows_op<Stream>(channel, output_info, result),
|
||||
token,
|
||||
channel
|
||||
);
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
void boost::mysql::detail::read_all_rows(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
execution_state& st,
|
||||
rows& output,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
// TODO: this can be optimized
|
||||
output = read_all_rows(channel, result, err, info);
|
||||
info.clear();
|
||||
output.clear();
|
||||
|
||||
// If the resultset_base is already complete, we don't need to read anything
|
||||
if (st.complete())
|
||||
{
|
||||
err = error_code();
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear anything from previous runs
|
||||
channel.shared_fields().clear();
|
||||
|
||||
while (!st.complete())
|
||||
{
|
||||
// Read from the stream until there is at least one message
|
||||
channel.read_some(err, true);
|
||||
if (err)
|
||||
return;
|
||||
|
||||
// Process read messages
|
||||
process_all_rows(channel, st, output, err, info);
|
||||
if (err)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
||||
boost::mysql::detail::async_read_all_rows(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
execution_state& st,
|
||||
rows& output,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
return boost::asio::async_compose<CompletionToken, void(error_code)>(
|
||||
read_all_rows_op_rows<Stream>(channel, output_info, result, output),
|
||||
read_all_rows_op<Stream>(channel, output_info, st, output),
|
||||
token,
|
||||
channel
|
||||
);
|
||||
|
|
|
@ -10,10 +10,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
#include <boost/mysql/row_view.hpp>
|
||||
|
||||
#include <boost/mysql/detail/network_algorithms/read_one_row.hpp>
|
||||
#include <boost/mysql/detail/protocol/deserialize_row.hpp>
|
||||
#include <boost/mysql/resultset_base.hpp>
|
||||
#include <boost/mysql/row_view.hpp>
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
|
@ -28,7 +29,7 @@ template <class Stream>
|
|||
inline row_view process_one_row(
|
||||
channel<Stream>& channel,
|
||||
boost::asio::const_buffer read_message,
|
||||
resultset_base& result,
|
||||
execution_state& st,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
|
@ -41,13 +42,13 @@ inline row_view process_one_row(
|
|||
read_message,
|
||||
channel.current_capabilities(),
|
||||
channel.buffer_first(),
|
||||
result,
|
||||
st,
|
||||
channel.shared_fields(),
|
||||
err,
|
||||
info
|
||||
);
|
||||
|
||||
if (!err && !result.complete())
|
||||
if (!err && !st.complete())
|
||||
{
|
||||
offsets_to_string_views(channel.shared_fields(), channel.buffer_first());
|
||||
return row_view(channel.shared_fields().data(), channel.shared_fields().size());
|
||||
|
@ -63,10 +64,10 @@ struct read_one_row_op : boost::asio::coroutine
|
|||
{
|
||||
channel<Stream>& chan_;
|
||||
error_info& output_info_;
|
||||
resultset_base& resultset_;
|
||||
execution_state& st_;
|
||||
|
||||
read_one_row_op(channel<Stream>& chan, error_info& output_info, resultset_base& result) noexcept
|
||||
: chan_(chan), output_info_(output_info), resultset_(result)
|
||||
read_one_row_op(channel<Stream>& chan, error_info& output_info, execution_state& st) noexcept
|
||||
: chan_(chan), output_info_(output_info), st_(st)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -87,7 +88,7 @@ struct read_one_row_op : boost::asio::coroutine
|
|||
output_info_.clear();
|
||||
|
||||
// If the resultset is already complete, we don't need to read anything
|
||||
if (resultset_.complete())
|
||||
if (st_.complete())
|
||||
{
|
||||
BOOST_ASIO_CORO_YIELD boost::asio::post(std::move(self));
|
||||
self.complete(error_code(), row_view());
|
||||
|
@ -95,61 +96,15 @@ struct read_one_row_op : boost::asio::coroutine
|
|||
}
|
||||
|
||||
// Read the message
|
||||
BOOST_ASIO_CORO_YIELD chan_.async_read_one(
|
||||
resultset_.sequence_number(),
|
||||
std::move(self)
|
||||
);
|
||||
BOOST_ASIO_CORO_YIELD chan_.async_read_one(st_.sequence_number(), std::move(self));
|
||||
|
||||
// Process it
|
||||
result = process_one_row(chan_, read_message, resultset_, err, output_info_);
|
||||
result = process_one_row(chan_, read_message, st_, err, output_info_);
|
||||
self.complete(err, result);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class Stream>
|
||||
struct read_one_row_op_row : boost::asio::coroutine
|
||||
{
|
||||
channel<Stream>& chan_;
|
||||
error_info& output_info_;
|
||||
resultset_base& resultset_;
|
||||
row& output_;
|
||||
|
||||
read_one_row_op_row(
|
||||
channel<Stream>& chan,
|
||||
error_info& output_info,
|
||||
resultset_base& result,
|
||||
row& output
|
||||
) noexcept
|
||||
: chan_(chan), output_info_(output_info), resultset_(result), output_(output)
|
||||
{
|
||||
}
|
||||
|
||||
template <class Self>
|
||||
void operator()(Self& self, error_code err = {}, row_view rv = {})
|
||||
{
|
||||
// Error checking
|
||||
if (err)
|
||||
{
|
||||
self.complete(err, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Normal path
|
||||
BOOST_ASIO_CORO_REENTER(*this)
|
||||
{
|
||||
// error_info already cleared by child ops
|
||||
output_.clear();
|
||||
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
async_read_one_row(chan_, resultset_, output_info_, std::move(self));
|
||||
|
||||
output_ = rv;
|
||||
self.complete(error_code(), !rv.empty());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
@ -157,7 +112,7 @@ struct read_one_row_op_row : boost::asio::coroutine
|
|||
template <class Stream>
|
||||
boost::mysql::row_view boost::mysql::detail::read_one_row(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
execution_state& result,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
|
@ -176,51 +131,23 @@ boost::mysql::row_view boost::mysql::detail::read_one_row(
|
|||
return process_one_row(channel, read_message, result, err, info);
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
bool boost::mysql::detail::read_one_row(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
row& output,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
// TODO: this can be optimized
|
||||
output = read_one_row(channel, result, err, info);
|
||||
return !output.empty();
|
||||
}
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, ::boost::mysql::row_view))
|
||||
CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
|
||||
CompletionToken,
|
||||
void(boost::mysql::error_code, boost::mysql::row_view)
|
||||
)
|
||||
boost::mysql::detail::async_read_one_row(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
execution_state& st,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
return boost::asio::async_compose<CompletionToken, void(error_code, row_view)>(
|
||||
read_one_row_op<Stream>(channel, output_info, result),
|
||||
token,
|
||||
channel
|
||||
);
|
||||
}
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code, bool))
|
||||
boost::mysql::detail::async_read_one_row(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
row& output,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
return boost::asio::async_compose<CompletionToken, void(error_code, bool)>(
|
||||
read_one_row_op_row<Stream>(channel, output_info, result, output),
|
||||
read_one_row_op<Stream>(channel, output_info, st),
|
||||
token,
|
||||
channel
|
||||
);
|
||||
|
|
|
@ -10,11 +10,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
#include <boost/mysql/row.hpp>
|
||||
|
||||
#include <boost/mysql/detail/network_algorithms/read_some_rows.hpp>
|
||||
#include <boost/mysql/detail/protocol/deserialize_row.hpp>
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/resultset_base.hpp>
|
||||
#include <boost/mysql/row.hpp>
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
|
@ -28,7 +29,7 @@ namespace detail {
|
|||
template <class Stream>
|
||||
inline rows_view process_some_rows(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
execution_state& st,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
|
@ -40,7 +41,7 @@ inline rows_view process_some_rows(
|
|||
while (channel.has_read_messages())
|
||||
{
|
||||
// Get the row message
|
||||
auto message = channel.next_read_message(result.sequence_number(), err);
|
||||
auto message = channel.next_read_message(st.sequence_number(), err);
|
||||
if (err)
|
||||
return rows_view();
|
||||
|
||||
|
@ -49,7 +50,7 @@ inline rows_view process_some_rows(
|
|||
message,
|
||||
channel.current_capabilities(),
|
||||
channel.buffer_first(),
|
||||
result,
|
||||
st,
|
||||
channel.shared_fields(),
|
||||
err,
|
||||
info
|
||||
|
@ -61,7 +62,7 @@ inline rows_view process_some_rows(
|
|||
// will point into the channel's internal buffer
|
||||
|
||||
// If we received an EOF, we're done
|
||||
if (result.complete())
|
||||
if (st.complete())
|
||||
break;
|
||||
++num_rows;
|
||||
}
|
||||
|
@ -69,8 +70,8 @@ inline rows_view process_some_rows(
|
|||
|
||||
return rows_view(
|
||||
channel.shared_fields().data(),
|
||||
num_rows * result.fields().size(),
|
||||
result.fields().size()
|
||||
num_rows * st.fields().size(),
|
||||
st.fields().size()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -79,14 +80,10 @@ struct read_some_rows_op : boost::asio::coroutine
|
|||
{
|
||||
channel<Stream>& chan_;
|
||||
error_info& output_info_;
|
||||
resultset_base& resultset_;
|
||||
execution_state& st_;
|
||||
|
||||
read_some_rows_op(
|
||||
channel<Stream>& chan,
|
||||
error_info& output_info,
|
||||
resultset_base& result
|
||||
) noexcept
|
||||
: chan_(chan), output_info_(output_info), resultset_(result)
|
||||
read_some_rows_op(channel<Stream>& chan, error_info& output_info, execution_state& st) noexcept
|
||||
: chan_(chan), output_info_(output_info), st_(st)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -107,7 +104,7 @@ struct read_some_rows_op : boost::asio::coroutine
|
|||
output_info_.clear();
|
||||
|
||||
// If the resultset is already complete, we don't need to read anything
|
||||
if (resultset_.complete())
|
||||
if (st_.complete())
|
||||
{
|
||||
BOOST_ASIO_CORO_YIELD boost::asio::post(std::move(self));
|
||||
self.complete(error_code(), rows_view());
|
||||
|
@ -118,56 +115,13 @@ struct read_some_rows_op : boost::asio::coroutine
|
|||
BOOST_ASIO_CORO_YIELD chan_.async_read_some(std::move(self));
|
||||
|
||||
// Process messages
|
||||
output = process_some_rows(chan_, resultset_, err, output_info_);
|
||||
output = process_some_rows(chan_, st_, err, output_info_);
|
||||
|
||||
self.complete(err, output);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class Stream>
|
||||
struct read_some_rows_op_rows : boost::asio::coroutine
|
||||
{
|
||||
channel<Stream>& chan_;
|
||||
error_info& output_info_;
|
||||
resultset_base& resultset_;
|
||||
rows& output_;
|
||||
|
||||
read_some_rows_op_rows(
|
||||
channel<Stream>& chan,
|
||||
error_info& output_info,
|
||||
resultset_base& result,
|
||||
rows& output
|
||||
) noexcept
|
||||
: chan_(chan), output_info_(output_info), resultset_(result), output_(output)
|
||||
{
|
||||
}
|
||||
|
||||
template <class Self>
|
||||
void operator()(Self& self, error_code err = {}, rows_view rv = {})
|
||||
{
|
||||
// Error checking
|
||||
if (err)
|
||||
{
|
||||
self.complete(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Normal path
|
||||
BOOST_ASIO_CORO_REENTER(*this)
|
||||
{
|
||||
// output_info already cleared by child ops
|
||||
output_.clear();
|
||||
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
async_read_some_rows(chan_, resultset_, output_info_, std::move(self));
|
||||
|
||||
output_ = rv;
|
||||
self.complete(err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
@ -175,13 +129,13 @@ struct read_some_rows_op_rows : boost::asio::coroutine
|
|||
template <class Stream>
|
||||
boost::mysql::rows_view boost::mysql::detail::read_some_rows(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
execution_state& st,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
// If the resultset is already complete, we don't need to read anything
|
||||
if (result.complete())
|
||||
if (st.complete())
|
||||
{
|
||||
return rows_view();
|
||||
}
|
||||
|
@ -192,53 +146,26 @@ boost::mysql::rows_view boost::mysql::detail::read_some_rows(
|
|||
return rows_view();
|
||||
|
||||
// Process read messages
|
||||
return process_some_rows(channel, result, err, info);
|
||||
return process_some_rows(channel, st, err, info);
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
void boost::mysql::detail::read_some_rows(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
rows& output,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
// TODO: this can be optimized
|
||||
output = read_some_rows(channel, result, err, info);
|
||||
}
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, ::boost::mysql::rows_view))
|
||||
CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
|
||||
CompletionToken,
|
||||
void(boost::mysql::error_code, boost::mysql::rows_view)
|
||||
)
|
||||
boost::mysql::detail::async_read_some_rows(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
execution_state& st,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
return boost::asio::async_compose<CompletionToken, void(error_code, rows_view)>(
|
||||
read_some_rows_op<Stream>(channel, output_info, result),
|
||||
token,
|
||||
channel
|
||||
);
|
||||
}
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
||||
boost::mysql::detail::async_read_some_rows(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
rows& output,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
return boost::asio::async_compose<CompletionToken, void(error_code)>(
|
||||
read_some_rows_op_rows<Stream>(channel, output_info, result, output),
|
||||
read_some_rows_op<Stream>(channel, output_info, st),
|
||||
token,
|
||||
channel
|
||||
);
|
||||
|
|
|
@ -5,18 +5,19 @@
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_EXECUTE_GENERIC_HPP
|
||||
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_EXECUTE_GENERIC_HPP
|
||||
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_START_EXECUTION_GENERIC_HPP
|
||||
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_START_EXECUTION_GENERIC_HPP
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
|
||||
#include <boost/mysql/detail/auxiliar/bytestring.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/execute_generic.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/start_execution_generic.hpp>
|
||||
#include <boost/mysql/detail/protocol/capabilities.hpp>
|
||||
#include <boost/mysql/detail/protocol/common_messages.hpp>
|
||||
#include <boost/mysql/detail/protocol/resultset_encoding.hpp>
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/resultset_base.hpp>
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
|
||||
|
@ -28,10 +29,10 @@ namespace boost {
|
|||
namespace mysql {
|
||||
namespace detail {
|
||||
|
||||
class execute_processor
|
||||
class start_execution_processor
|
||||
{
|
||||
resultset_encoding encoding_;
|
||||
resultset_base& output_;
|
||||
execution_state& st_;
|
||||
error_info& output_info_;
|
||||
bytestring& write_buffer_;
|
||||
capabilities caps_;
|
||||
|
@ -39,88 +40,44 @@ class execute_processor
|
|||
std::size_t remaining_fields_{};
|
||||
|
||||
public:
|
||||
execute_processor(
|
||||
start_execution_processor(
|
||||
resultset_encoding encoding,
|
||||
resultset_base& output,
|
||||
execution_state& st,
|
||||
error_info& output_info,
|
||||
bytestring& write_buffer,
|
||||
capabilities caps
|
||||
) noexcept
|
||||
: encoding_(encoding),
|
||||
output_(output),
|
||||
st_(st),
|
||||
output_info_(output_info),
|
||||
write_buffer_(write_buffer),
|
||||
caps_(caps){};
|
||||
|
||||
void clear_output_info() noexcept { output_info_.clear(); }
|
||||
|
||||
template <BOOST_MYSQL_EXECUTE_REQUEST Request, class Stream>
|
||||
void process_request(const Request& request, channel<Stream>& chan)
|
||||
template <BOOST_MYSQL_SERIALIZE_FN SerializeFn>
|
||||
void process_request(SerializeFn&& request)
|
||||
{
|
||||
output_.reset(&chan, encoding_);
|
||||
serialize_message(request, caps_, write_buffer_);
|
||||
st_.reset(encoding_);
|
||||
std::forward<SerializeFn>(request)(caps_, write_buffer_);
|
||||
// serialize_message(request, caps_, write_buffer_);
|
||||
}
|
||||
|
||||
template <BOOST_MYSQL_EXECUTE_REQUEST_MAKER RequestMaker, class Stream>
|
||||
void process_request_maker(const RequestMaker& reqmaker, channel<Stream>& chan)
|
||||
void process_response(boost::asio::const_buffer msg, error_code& err)
|
||||
{
|
||||
auto st = reqmaker.make_storage();
|
||||
process_request(reqmaker.make_request(st), chan);
|
||||
}
|
||||
|
||||
void process_response(boost::asio::const_buffer response, error_code& err)
|
||||
{
|
||||
// Response may be: ok_packet, err_packet, local infile request (not implemented)
|
||||
// If it is none of this, then the message type itself is the beginning of
|
||||
// a length-encoded int containing the field count
|
||||
deserialization_context ctx(response, caps_);
|
||||
std::uint8_t msg_type = 0;
|
||||
err = make_error_code(deserialize(ctx, msg_type));
|
||||
if (err)
|
||||
return;
|
||||
if (msg_type == ok_packet_header)
|
||||
auto response = deserialize_execute_response(msg, caps_, output_info_);
|
||||
switch (response.type)
|
||||
{
|
||||
ok_packet ok_pack;
|
||||
err = deserialize_message(ctx, ok_pack);
|
||||
if (err)
|
||||
return;
|
||||
output_.complete(ok_pack);
|
||||
case execute_response::type_t::error: err = response.data.err; break;
|
||||
case execute_response::type_t::ok_packet:
|
||||
st_.complete(response.data.ok_pack);
|
||||
num_fields_ = 0;
|
||||
}
|
||||
else if (msg_type == error_packet_header)
|
||||
{
|
||||
err = process_error_packet(ctx, output_info_);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Resultset with metadata. First packet is an int_lenenc with
|
||||
// the number of field definitions to expect. Message type is part
|
||||
// of this packet, so we must rewind the context
|
||||
ctx.rewind(1);
|
||||
int_lenenc num_fields;
|
||||
err = deserialize_message(ctx, num_fields);
|
||||
if (err)
|
||||
return;
|
||||
|
||||
// For platforms where size_t is shorter than uint64_t,
|
||||
// perform range check
|
||||
if (num_fields.value > std::numeric_limits<std::size_t>::max())
|
||||
{
|
||||
err = make_error_code(errc::protocol_value_error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure we have fields, as field_count is indicative of
|
||||
// a resultset with fields
|
||||
num_fields_ = static_cast<std::size_t>(num_fields.value);
|
||||
if (num_fields_ == 0)
|
||||
{
|
||||
err = make_error_code(errc::protocol_value_error);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
case execute_response::type_t::num_fields:
|
||||
num_fields_ = response.data.num_fields;
|
||||
remaining_fields_ = num_fields_;
|
||||
output_.prepare_meta(num_fields_);
|
||||
st_.prepare_meta(num_fields_);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,29 +90,30 @@ public:
|
|||
return err;
|
||||
|
||||
// Add it to our array
|
||||
output_.add_meta(field_definition);
|
||||
st_.add_meta(field_definition);
|
||||
--remaining_fields_;
|
||||
return error_code();
|
||||
}
|
||||
|
||||
std::uint8_t& sequence_number() noexcept { return output_.sequence_number(); }
|
||||
std::uint8_t& sequence_number() noexcept { return st_.sequence_number(); }
|
||||
std::size_t num_fields() const noexcept { return num_fields_; }
|
||||
bool has_remaining_fields() const noexcept { return remaining_fields_ != 0; }
|
||||
};
|
||||
|
||||
template <class Stream, BOOST_MYSQL_EXECUTE_REQUEST_MAKER RequestMaker>
|
||||
struct execute_generic_op : boost::asio::coroutine
|
||||
template <class Stream, BOOST_MYSQL_SERIALIZE_FN SerializeFn>
|
||||
struct start_execution_generic_op : boost::asio::coroutine
|
||||
{
|
||||
channel<Stream>& chan_;
|
||||
RequestMaker reqmaker_;
|
||||
execute_processor processor_;
|
||||
SerializeFn serialize_fn_;
|
||||
start_execution_processor processor_;
|
||||
|
||||
execute_generic_op(
|
||||
template <class DeducedSerializeFn>
|
||||
start_execution_generic_op(
|
||||
channel<Stream>& chan,
|
||||
RequestMaker&& reqmaker,
|
||||
const execute_processor& processor
|
||||
DeducedSerializeFn&& fn,
|
||||
const start_execution_processor& processor
|
||||
)
|
||||
: chan_(chan), reqmaker_(std::move(reqmaker)), processor_(processor)
|
||||
: chan_(chan), serialize_fn_(std::forward<DeducedSerializeFn>(fn)), processor_(processor)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -175,7 +133,7 @@ struct execute_generic_op : boost::asio::coroutine
|
|||
processor_.clear_output_info();
|
||||
|
||||
// Serialize the request
|
||||
processor_.process_request_maker(reqmaker_, chan_);
|
||||
processor_.process_request(std::move(serialize_fn_));
|
||||
|
||||
// Send it
|
||||
BOOST_ASIO_CORO_YIELD chan_
|
||||
|
@ -232,20 +190,72 @@ struct execute_generic_op : boost::asio::coroutine
|
|||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
template <class Stream, BOOST_MYSQL_EXECUTE_REQUEST Request>
|
||||
void boost::mysql::detail::execute_generic(
|
||||
inline boost::mysql::detail::execute_response boost::mysql::detail::deserialize_execute_response(
|
||||
boost::asio::const_buffer msg,
|
||||
capabilities caps,
|
||||
error_info& info
|
||||
) noexcept
|
||||
{
|
||||
// Response may be: ok_packet, err_packet, local infile request (not implemented)
|
||||
// If it is none of this, then the message type itself is the beginning of
|
||||
// a length-encoded int containing the field count
|
||||
deserialization_context ctx(msg, caps);
|
||||
std::uint8_t msg_type = 0;
|
||||
error_code err = make_error_code(deserialize(ctx, msg_type));
|
||||
if (err)
|
||||
return err;
|
||||
if (msg_type == ok_packet_header)
|
||||
{
|
||||
ok_packet ok_pack;
|
||||
err = deserialize_message(ctx, ok_pack);
|
||||
if (err)
|
||||
return err;
|
||||
return ok_pack;
|
||||
}
|
||||
else if (msg_type == error_packet_header)
|
||||
{
|
||||
return process_error_packet(ctx, info);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Resultset with metadata. First packet is an int_lenenc with
|
||||
// the number of field definitions to expect. Message type is part
|
||||
// of this packet, so we must rewind the context
|
||||
ctx.rewind(1);
|
||||
int_lenenc num_fields;
|
||||
err = deserialize_message(ctx, num_fields);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
// We should have at least one field.
|
||||
// The max number of fields is some value around 1024. For simplicity/extensibility,
|
||||
// we accept anything less than 0xffff
|
||||
if (num_fields.value == 0 || num_fields.value > 0xffffu)
|
||||
{
|
||||
return make_error_code(errc::protocol_value_error);
|
||||
}
|
||||
|
||||
return static_cast<std::size_t>(num_fields.value);
|
||||
}
|
||||
}
|
||||
|
||||
template <class Stream, BOOST_MYSQL_SERIALIZE_FN SerializeFn>
|
||||
void boost::mysql::detail::start_execution_generic(
|
||||
resultset_encoding encoding,
|
||||
channel<Stream>& channel,
|
||||
const Request& request,
|
||||
resultset_base& output,
|
||||
const SerializeFn& fn,
|
||||
execution_state& st,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
// Clear info
|
||||
info.clear();
|
||||
|
||||
// Serialize the request
|
||||
execute_processor
|
||||
processor(encoding, output, info, channel.shared_buffer(), channel.current_capabilities());
|
||||
processor.process_request(request, channel);
|
||||
start_execution_processor
|
||||
processor(encoding, st, info, channel.shared_buffer(), channel.current_capabilities());
|
||||
processor.process_request(fn);
|
||||
|
||||
// Send it
|
||||
channel.write(channel.shared_buffer(), processor.sequence_number(), err);
|
||||
|
@ -286,24 +296,27 @@ void boost::mysql::detail::execute_generic(
|
|||
}
|
||||
}
|
||||
|
||||
template <class Stream, BOOST_MYSQL_EXECUTE_REQUEST_MAKER RequestMaker, class CompletionToken>
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_MYSQL_SERIALIZE_FN SerializeFn,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
||||
boost::mysql::detail::async_execute_generic(
|
||||
boost::mysql::detail::async_start_execution_generic(
|
||||
resultset_encoding encoding,
|
||||
channel<Stream>& channel,
|
||||
RequestMaker&& reqmaker,
|
||||
resultset_base& output,
|
||||
SerializeFn&& fn,
|
||||
execution_state& st,
|
||||
error_info& info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
return boost::asio::async_compose<CompletionToken, void(error_code)>(
|
||||
execute_generic_op<Stream, typename std::decay<RequestMaker>::type>(
|
||||
start_execution_generic_op<Stream, typename std::decay<SerializeFn>::type>(
|
||||
channel,
|
||||
std::move(reqmaker),
|
||||
execute_processor(
|
||||
std::forward<SerializeFn>(fn),
|
||||
start_execution_processor(
|
||||
encoding,
|
||||
output,
|
||||
st,
|
||||
info,
|
||||
channel.shared_buffer(),
|
||||
channel.current_capabilities()
|
|
@ -5,15 +5,18 @@
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_EXECUTE_QUERY_HPP
|
||||
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_EXECUTE_QUERY_HPP
|
||||
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_START_QUERY_HPP
|
||||
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_START_QUERY_HPP
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/mysql/detail/network_algorithms/execute_generic.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/execute_query.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/start_execution_generic.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/start_query.hpp>
|
||||
#include <boost/mysql/detail/protocol/capabilities.hpp>
|
||||
#include <boost/mysql/detail/protocol/protocol_types.hpp>
|
||||
#include <boost/mysql/detail/protocol/query_messages.hpp>
|
||||
#include <boost/mysql/detail/protocol/resultset_encoding.hpp>
|
||||
#include <boost/mysql/detail/protocol/serialization.hpp>
|
||||
|
||||
#include <boost/utility/string_view_fwd.hpp>
|
||||
|
||||
|
@ -21,20 +24,16 @@ namespace boost {
|
|||
namespace mysql {
|
||||
namespace detail {
|
||||
|
||||
class query_request_maker
|
||||
class query_serialize_fn
|
||||
{
|
||||
boost::string_view query_;
|
||||
|
||||
public:
|
||||
struct storage_type
|
||||
query_serialize_fn(boost::string_view query) noexcept : query_(query) {}
|
||||
void operator()(capabilities caps, std::vector<std::uint8_t>& buffer) const
|
||||
{
|
||||
};
|
||||
|
||||
query_request_maker(boost::string_view query) noexcept : query_(query) {}
|
||||
storage_type make_storage() const noexcept { return storage_type(); }
|
||||
com_query_packet make_request(storage_type) const noexcept
|
||||
{
|
||||
return com_query_packet{string_eof(query_)};
|
||||
com_query_packet request{string_eof(query_)};
|
||||
serialize_message(request, caps, buffer);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -43,38 +42,40 @@ public:
|
|||
} // namespace boost
|
||||
|
||||
template <class Stream>
|
||||
void boost::mysql::detail::execute_query(
|
||||
void boost::mysql::detail::start_query(
|
||||
channel<Stream>& channel,
|
||||
boost::string_view query,
|
||||
resultset_base& output,
|
||||
execution_state& output,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
execute_generic(
|
||||
start_execution_generic(
|
||||
resultset_encoding::text,
|
||||
channel,
|
||||
com_query_packet{string_eof(query)},
|
||||
query_serialize_fn(query),
|
||||
output,
|
||||
err,
|
||||
info
|
||||
);
|
||||
}
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
||||
boost::mysql::detail::async_execute_query(
|
||||
boost::mysql::detail::async_start_query(
|
||||
channel<Stream>& chan,
|
||||
boost::string_view query,
|
||||
resultset_base& output,
|
||||
execution_state& output,
|
||||
error_info& info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
return async_execute_generic(
|
||||
return async_start_execution_generic(
|
||||
resultset_encoding::text,
|
||||
chan,
|
||||
query_request_maker(query),
|
||||
query_serialize_fn(query),
|
||||
output,
|
||||
info,
|
||||
std::forward<CompletionToken>(token)
|
|
@ -0,0 +1,285 @@
|
|||
//
|
||||
// Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_START_STATEMENT_EXECUTION_HPP
|
||||
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_START_STATEMENT_EXECUTION_HPP
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
#include <boost/mysql/field_view.hpp>
|
||||
#include <boost/mysql/statement_base.hpp>
|
||||
|
||||
#include <boost/mysql/detail/auxiliar/stringize.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/start_execution_generic.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/start_statement_execution.hpp>
|
||||
#include <boost/mysql/detail/protocol/capabilities.hpp>
|
||||
#include <boost/mysql/detail/protocol/prepared_statement_messages.hpp>
|
||||
#include <boost/mysql/detail/protocol/resultset_encoding.hpp>
|
||||
#include <boost/mysql/detail/protocol/serialization.hpp>
|
||||
|
||||
#include <boost/asio/compose.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/mp11/integer_sequence.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
||||
template <BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator>
|
||||
com_stmt_execute_packet<FieldViewFwdIterator> make_stmt_execute_packet(
|
||||
std::uint32_t stmt_id,
|
||||
FieldViewFwdIterator params_first,
|
||||
FieldViewFwdIterator params_last
|
||||
) noexcept(std::is_nothrow_copy_constructible<FieldViewFwdIterator>::value)
|
||||
{
|
||||
return com_stmt_execute_packet<FieldViewFwdIterator>{
|
||||
stmt_id,
|
||||
std::uint8_t(0), // flags
|
||||
std::uint32_t(1), // iteration count
|
||||
std::uint8_t(1), // new params flag: set
|
||||
params_first,
|
||||
params_last,
|
||||
};
|
||||
}
|
||||
|
||||
template <BOOST_MYSQL_FIELD_LIKE... T, std::size_t... I>
|
||||
std::array<field_view, sizeof...(T)> tuple_to_array_impl(const std::tuple<T...>& t, boost::mp11::index_sequence<I...>) noexcept
|
||||
{
|
||||
return std::array<field_view, sizeof...(T)>{{field_view(std::get<I>(t))...}};
|
||||
}
|
||||
|
||||
template <BOOST_MYSQL_FIELD_LIKE... T>
|
||||
std::array<field_view, sizeof...(T)> tuple_to_array(const std::tuple<T...>& t) noexcept
|
||||
{
|
||||
return tuple_to_array_impl(t, boost::mp11::make_index_sequence<sizeof...(T)>());
|
||||
}
|
||||
|
||||
template <BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator>
|
||||
class stmt_execute_it_serialize_fn
|
||||
{
|
||||
std::uint32_t stmt_id_;
|
||||
FieldViewFwdIterator first_;
|
||||
FieldViewFwdIterator last_;
|
||||
|
||||
public:
|
||||
stmt_execute_it_serialize_fn(
|
||||
std::uint32_t stmt_id,
|
||||
FieldViewFwdIterator first,
|
||||
FieldViewFwdIterator last
|
||||
) noexcept(std::is_nothrow_copy_constructible<FieldViewFwdIterator>::value)
|
||||
: stmt_id_(stmt_id), first_(first), last_(last)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(capabilities caps, std::vector<std::uint8_t>& buffer) const
|
||||
{
|
||||
auto request = make_stmt_execute_packet(stmt_id_, first_, last_);
|
||||
serialize_message(request, caps, buffer);
|
||||
}
|
||||
};
|
||||
|
||||
template <BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple>
|
||||
class stmt_execute_tuple_serialize_fn
|
||||
{
|
||||
std::uint32_t stmt_id_;
|
||||
FieldLikeTuple params_;
|
||||
|
||||
public:
|
||||
// We need a deduced context to enable perfect forwarding
|
||||
template <BOOST_MYSQL_FIELD_LIKE_TUPLE DeducedTuple>
|
||||
stmt_execute_tuple_serialize_fn(std::uint32_t stmt_id, DeducedTuple&& params) noexcept(
|
||||
std::is_nothrow_constructible<
|
||||
FieldLikeTuple,
|
||||
decltype(std::forward<DeducedTuple>(params))>::value
|
||||
)
|
||||
: stmt_id_(stmt_id), params_(std::forward<DeducedTuple>(params))
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(capabilities caps, std::vector<std::uint8_t>& buffer) const
|
||||
{
|
||||
auto field_views = tuple_to_array(params_);
|
||||
auto request = make_stmt_execute_packet(stmt_id_, field_views.begin(), field_views.end());
|
||||
serialize_message(request, caps, buffer);
|
||||
}
|
||||
};
|
||||
|
||||
inline error_code check_num_params(const statement_base& stmt, std::size_t param_count)
|
||||
{
|
||||
if (param_count != stmt.num_params())
|
||||
{
|
||||
return make_error_code(errc::wrong_num_params);
|
||||
}
|
||||
else
|
||||
{
|
||||
return error_code();
|
||||
}
|
||||
}
|
||||
|
||||
template <BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator>
|
||||
error_code check_num_params(
|
||||
const statement_base& stmt,
|
||||
FieldViewFwdIterator params_first,
|
||||
FieldViewFwdIterator params_last
|
||||
)
|
||||
{
|
||||
return check_num_params(stmt, std::distance(params_first, params_last));
|
||||
}
|
||||
|
||||
struct fast_fail_op : boost::asio::coroutine
|
||||
{
|
||||
error_code err_;
|
||||
error_info& output_info_;
|
||||
|
||||
fast_fail_op(error_code err, error_info& info) noexcept : err_(err), output_info_(info) {}
|
||||
|
||||
template <class Self>
|
||||
void operator()(Self& self)
|
||||
{
|
||||
BOOST_ASIO_CORO_REENTER(*this)
|
||||
{
|
||||
BOOST_ASIO_CORO_YIELD boost::asio::post(std::move(self));
|
||||
output_info_.clear();
|
||||
self.complete(err_);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
template <class Stream, BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator>
|
||||
void boost::mysql::detail::start_statement_execution(
|
||||
channel<Stream>& chan,
|
||||
const statement_base& stmt,
|
||||
FieldViewFwdIterator params_first,
|
||||
FieldViewFwdIterator params_last,
|
||||
execution_state& output,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
err = check_num_params(stmt, params_first, params_last);
|
||||
if (!err)
|
||||
{
|
||||
start_execution_generic(
|
||||
resultset_encoding::binary,
|
||||
chan,
|
||||
stmt_execute_it_serialize_fn<FieldViewFwdIterator>(
|
||||
stmt.id(),
|
||||
params_first,
|
||||
params_last
|
||||
),
|
||||
output,
|
||||
err,
|
||||
info
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
||||
boost::mysql::detail::async_start_statement_execution(
|
||||
channel<Stream>& chan,
|
||||
const statement_base& stmt,
|
||||
FieldViewFwdIterator params_first,
|
||||
FieldViewFwdIterator params_last,
|
||||
execution_state& output,
|
||||
error_info& info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
error_code err = check_num_params(stmt, params_first, params_last);
|
||||
if (err)
|
||||
{
|
||||
return boost::asio::async_compose<CompletionToken, void(error_code)>(
|
||||
fast_fail_op(err, info),
|
||||
token,
|
||||
chan
|
||||
);
|
||||
}
|
||||
return async_start_execution_generic(
|
||||
resultset_encoding::binary,
|
||||
chan,
|
||||
stmt_execute_it_serialize_fn<FieldViewFwdIterator>(stmt.id(), params_first, params_last),
|
||||
output,
|
||||
info,
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
}
|
||||
|
||||
template <class Stream, BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple>
|
||||
void boost::mysql::detail::start_statement_execution(
|
||||
channel<Stream>& channel,
|
||||
const statement_base& stmt,
|
||||
const FieldLikeTuple& params,
|
||||
execution_state& output,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
auto params_array = tuple_to_array(params);
|
||||
start_statement_execution(
|
||||
channel,
|
||||
stmt,
|
||||
params_array.begin(),
|
||||
params_array.end(),
|
||||
output,
|
||||
err,
|
||||
info
|
||||
);
|
||||
}
|
||||
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
||||
boost::mysql::detail::async_start_statement_execution(
|
||||
channel<Stream>& chan,
|
||||
const statement_base& stmt,
|
||||
FieldLikeTuple&& params,
|
||||
execution_state& output,
|
||||
error_info& info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
using decayed_tuple = typename std::decay<FieldLikeTuple>::type;
|
||||
error_code err = check_num_params(stmt, std::tuple_size<decayed_tuple>::value);
|
||||
if (err)
|
||||
{
|
||||
return boost::asio::async_compose<CompletionToken, void(error_code)>(
|
||||
fast_fail_op(err, info),
|
||||
token,
|
||||
chan
|
||||
);
|
||||
}
|
||||
return async_start_execution_generic(
|
||||
resultset_encoding::binary,
|
||||
chan,
|
||||
stmt_execute_tuple_serialize_fn<decayed_tuple>(
|
||||
stmt.id(),
|
||||
std::forward<FieldLikeTuple>(params)
|
||||
),
|
||||
output,
|
||||
info,
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
}
|
||||
|
||||
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_EXECUTE_STATEMENT_HPP_ */
|
|
@ -8,10 +8,11 @@
|
|||
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_PREPARE_STATEMENT_HPP
|
||||
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_PREPARE_STATEMENT_HPP
|
||||
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/statement_base.hpp>
|
||||
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
|
||||
#include <boost/utility/string_view.hpp>
|
||||
|
||||
namespace boost {
|
||||
|
@ -27,7 +28,9 @@ void prepare_statement(
|
|||
error_info& info
|
||||
);
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_prepare_statement(
|
||||
channel<Stream>& chan,
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_QUERY_HPP
|
||||
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_QUERY_HPP
|
||||
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/resultset.hpp>
|
||||
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
|
||||
#include <boost/asio/async_result.hpp>
|
||||
#include <boost/utility/string_view.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
||||
template <class Stream>
|
||||
void query(
|
||||
channel<Stream>& channel,
|
||||
boost::string_view query,
|
||||
resultset& output,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
);
|
||||
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_query(
|
||||
channel<Stream>& chan,
|
||||
boost::string_view query,
|
||||
resultset& output,
|
||||
error_info& info,
|
||||
CompletionToken&& token
|
||||
);
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#include <boost/mysql/detail/network_algorithms/impl/query.hpp>
|
||||
|
||||
#endif
|
|
@ -8,9 +8,10 @@
|
|||
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_QUIT_CONNECTION_HPP
|
||||
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_QUIT_CONNECTION_HPP
|
||||
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
#include <boost/mysql/error.hpp>
|
||||
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
@ -18,7 +19,9 @@ namespace detail {
|
|||
template <class Stream>
|
||||
void quit_connection(channel<Stream>& chan, error_code& code, error_info& info);
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_quit_connection(channel<Stream>& chan, CompletionToken&& token, error_info& info);
|
||||
|
||||
|
|
|
@ -8,47 +8,33 @@
|
|||
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_READ_ALL_ROWS_HPP
|
||||
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_READ_ALL_ROWS_HPP
|
||||
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/resultset_base.hpp>
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
#include <boost/mysql/rows.hpp>
|
||||
#include <boost/mysql/rows_view.hpp>
|
||||
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
||||
template <class Stream>
|
||||
rows_view read_all_rows(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
);
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, rows_view))
|
||||
async_read_all_rows(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
);
|
||||
|
||||
template <class Stream>
|
||||
void read_all_rows(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
execution_state& st,
|
||||
rows& output,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
);
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_read_all_rows(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
execution_state& st,
|
||||
rows& output,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
|
|
|
@ -8,11 +8,12 @@
|
|||
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_READ_ONE_ROW_HPP
|
||||
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_READ_ONE_ROW_HPP
|
||||
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/resultset_base.hpp>
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
#include <boost/mysql/row.hpp>
|
||||
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
@ -20,35 +21,19 @@ namespace detail {
|
|||
template <class Stream>
|
||||
row_view read_one_row(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
execution_state& st,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
);
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, ::boost::mysql::row_view))
|
||||
CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, row_view))
|
||||
async_read_one_row(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
);
|
||||
|
||||
template <class Stream>
|
||||
bool read_one_row(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
row& output,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
);
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, bool))
|
||||
async_read_one_row(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
row& output,
|
||||
execution_state& st,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
);
|
||||
|
|
|
@ -8,12 +8,13 @@
|
|||
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_READ_SOME_ROWS_HPP
|
||||
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_READ_SOME_ROWS_HPP
|
||||
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/resultset_base.hpp>
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
#include <boost/mysql/rows.hpp>
|
||||
#include <boost/mysql/rows_view.hpp>
|
||||
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
@ -21,44 +22,19 @@ namespace detail {
|
|||
template <class Stream>
|
||||
rows_view read_some_rows(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
execution_state& st,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
);
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, ::boost::mysql::rows_view))
|
||||
CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, rows_view))
|
||||
async_read_some_rows(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
);
|
||||
|
||||
template <class Stream>
|
||||
void read_some_rows(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
rows& output,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
);
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, rows_view))
|
||||
async_read_some_rows(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
);
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_read_some_rows(
|
||||
channel<Stream>& channel,
|
||||
resultset_base& result,
|
||||
rows& output,
|
||||
execution_state& result,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
);
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
//
|
||||
// Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_START_EXECUTION_GENERIC_HPP
|
||||
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_START_EXECUTION_GENERIC_HPP
|
||||
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
|
||||
#include <boost/mysql/detail/auxiliar/field_type_traits.hpp>
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
#include <boost/mysql/detail/config.hpp>
|
||||
#include <boost/mysql/detail/protocol/capabilities.hpp>
|
||||
#include <boost/mysql/detail/protocol/common_messages.hpp>
|
||||
#include <boost/mysql/detail/protocol/prepared_statement_messages.hpp>
|
||||
#include <boost/mysql/detail/protocol/query_messages.hpp>
|
||||
#include <boost/mysql/detail/protocol/resultset_encoding.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#ifdef BOOST_MYSQL_HAS_CONCEPTS
|
||||
#include <concepts>
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
||||
// Exposed for the sake of testing
|
||||
struct execute_response
|
||||
{
|
||||
enum class type_t
|
||||
{
|
||||
num_fields,
|
||||
ok_packet,
|
||||
error
|
||||
} type;
|
||||
union data_t
|
||||
{
|
||||
static_assert(std::is_trivially_destructible<error_code>::value, "");
|
||||
|
||||
std::size_t num_fields;
|
||||
ok_packet ok_pack;
|
||||
error_code err;
|
||||
|
||||
data_t(size_t v) noexcept : num_fields(v) {}
|
||||
data_t(const ok_packet& v) noexcept : ok_pack(v) {}
|
||||
data_t(error_code v) noexcept : err(v) {}
|
||||
} data;
|
||||
|
||||
execute_response(std::size_t v) noexcept : type(type_t::num_fields), data(v) {}
|
||||
execute_response(const ok_packet& v) noexcept : type(type_t::ok_packet), data(v) {}
|
||||
execute_response(error_code v) noexcept : type(type_t::error), data(v) {}
|
||||
};
|
||||
inline execute_response deserialize_execute_response(
|
||||
boost::asio::const_buffer msg,
|
||||
capabilities caps,
|
||||
error_info& info
|
||||
) noexcept;
|
||||
|
||||
#ifdef BOOST_MYSQL_HAS_CONCEPTS
|
||||
|
||||
template <class T>
|
||||
concept serialize_fn = std::invocable<std::decay_t<T>, capabilities, std::vector<std::uint8_t>&>;
|
||||
|
||||
#define BOOST_MYSQL_SERIALIZE_FN ::boost::mysql::detail::serialize_fn
|
||||
|
||||
#else // BOOST_MYSQL_HAS_CONCEPTS
|
||||
|
||||
#define BOOST_MYSQL_SERIALIZE_FN class
|
||||
|
||||
#endif // BOOST_MYSQL_HAS_CONCEPTS
|
||||
|
||||
// The sync version gets passed directlty the request packet to be serialized.
|
||||
// There is no need to defer the serialization here.
|
||||
template <class Stream, BOOST_MYSQL_SERIALIZE_FN SerializeFn>
|
||||
void start_execution_generic(
|
||||
resultset_encoding encoding,
|
||||
channel<Stream>& channel,
|
||||
const SerializeFn& fn,
|
||||
execution_state& st,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
);
|
||||
|
||||
// The async version gets passed a request maker, holding enough data to create
|
||||
// the request packet when the async op is started. Used by statement execution
|
||||
// with tuple params, to make lifetimes more user-friendly when using deferred tokens.
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_MYSQL_SERIALIZE_FN SerializeFn,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_start_execution_generic(
|
||||
resultset_encoding encoding,
|
||||
channel<Stream>& chan,
|
||||
SerializeFn&& fn,
|
||||
execution_state& st,
|
||||
error_info& info,
|
||||
CompletionToken&& token
|
||||
);
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#include <boost/mysql/detail/network_algorithms/impl/start_execution_generic.hpp>
|
||||
|
||||
#endif /* INCLUDE_MYSQL_IMPL_NETWORK_ALGORITHMS_READ_RESULTSET_HEAD_HPP_ */
|
|
@ -5,13 +5,15 @@
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_EXECUTE_QUERY_HPP
|
||||
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_EXECUTE_QUERY_HPP
|
||||
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_START_QUERY_HPP
|
||||
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_START_QUERY_HPP
|
||||
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/resultset_base.hpp>
|
||||
|
||||
#include <boost/asio/async_result.hpp>
|
||||
#include <boost/utility/string_view.hpp>
|
||||
|
||||
namespace boost {
|
||||
|
@ -19,20 +21,22 @@ namespace mysql {
|
|||
namespace detail {
|
||||
|
||||
template <class Stream>
|
||||
void execute_query(
|
||||
void start_query(
|
||||
channel<Stream>& channel,
|
||||
boost::string_view query,
|
||||
resultset_base& output,
|
||||
execution_state& output,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
);
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_execute_query(
|
||||
async_start_query(
|
||||
channel<Stream>& chan,
|
||||
boost::string_view query,
|
||||
resultset_base& output,
|
||||
execution_state& output,
|
||||
error_info& info,
|
||||
CompletionToken&& token
|
||||
);
|
||||
|
@ -41,6 +45,6 @@ async_execute_query(
|
|||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#include <boost/mysql/detail/network_algorithms/impl/execute_query.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/impl/start_query.hpp>
|
||||
|
||||
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_EXECUTE_QUERY_HPP_ */
|
|
@ -0,0 +1,80 @@
|
|||
//
|
||||
// Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_START_STATEMENT_EXECUTION_HPP
|
||||
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_START_STATEMENT_EXECUTION_HPP
|
||||
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
#include <boost/mysql/statement_base.hpp>
|
||||
|
||||
#include <boost/mysql/detail/auxiliar/field_type_traits.hpp>
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
|
||||
#include <boost/asio/async_result.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
||||
template <class Stream, BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator>
|
||||
void start_statement_execution(
|
||||
channel<Stream>& channel,
|
||||
const statement_base& stmt,
|
||||
FieldViewFwdIterator params_first,
|
||||
FieldViewFwdIterator params_last,
|
||||
execution_state& output,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
);
|
||||
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_start_statement_execution(
|
||||
channel<Stream>& chan,
|
||||
const statement_base& stmt,
|
||||
FieldViewFwdIterator params_first,
|
||||
FieldViewFwdIterator params_last,
|
||||
execution_state& output,
|
||||
error_info& info,
|
||||
CompletionToken&& token
|
||||
);
|
||||
|
||||
template <class Stream, BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple>
|
||||
void start_statement_execution(
|
||||
channel<Stream>& channel,
|
||||
const statement_base& stmt,
|
||||
const FieldLikeTuple& params,
|
||||
execution_state& output,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
);
|
||||
|
||||
template <
|
||||
class Stream,
|
||||
BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_start_statement_execution(
|
||||
channel<Stream>& chan,
|
||||
const statement_base& stmt,
|
||||
FieldLikeTuple&& params,
|
||||
execution_state& output,
|
||||
error_info& info,
|
||||
CompletionToken&& token
|
||||
);
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#include <boost/mysql/detail/network_algorithms/impl/start_statement_execution.hpp>
|
||||
|
||||
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_EXECUTE_STATEMENT_HPP_ */
|
|
@ -9,6 +9,7 @@
|
|||
#define BOOST_MYSQL_DETAIL_PROTOCOL_COMMON_MESSAGES_HPP
|
||||
|
||||
#include <boost/mysql/collation.hpp>
|
||||
|
||||
#include <boost/mysql/detail/protocol/constants.hpp>
|
||||
#include <boost/mysql/detail/protocol/serialization.hpp>
|
||||
|
||||
|
@ -87,8 +88,6 @@ struct err_packet
|
|||
}
|
||||
};
|
||||
|
||||
static_assert(is_struct_with_fields<err_packet>(), "Bad!");
|
||||
|
||||
// col def
|
||||
struct column_definition_packet
|
||||
{
|
||||
|
|
|
@ -8,12 +8,13 @@
|
|||
#ifndef BOOST_MYSQL_DETAIL_PROTOCOL_DESERIALIZE_ROW_HPP
|
||||
#define BOOST_MYSQL_DETAIL_PROTOCOL_DESERIALIZE_ROW_HPP
|
||||
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
#include <boost/mysql/field_view.hpp>
|
||||
|
||||
#include <boost/mysql/detail/protocol/capabilities.hpp>
|
||||
#include <boost/mysql/detail/protocol/resultset_encoding.hpp>
|
||||
#include <boost/mysql/detail/protocol/serialization_context.hpp>
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/field_view.hpp>
|
||||
#include <boost/mysql/resultset_base.hpp>
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
|
||||
|
@ -37,7 +38,7 @@ inline void deserialize_row(
|
|||
boost::asio::const_buffer read_message,
|
||||
capabilities current_capabilities,
|
||||
const std::uint8_t* buffer_first, // to store strings as offsets and allow buffer reallocation
|
||||
resultset_base& result, // should be valid() and !complete()
|
||||
execution_state& st, // should be !complete()
|
||||
std::vector<field_view>& output,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
|
|
|
@ -136,14 +136,13 @@ void boost::mysql::detail::deserialize_row(
|
|||
boost::asio::const_buffer read_message,
|
||||
capabilities current_capabilities,
|
||||
const std::uint8_t* buffer_first, // to store strings as offsets and allow buffer reallocation
|
||||
resultset_base& result,
|
||||
execution_state& st,
|
||||
std::vector<field_view>& output,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
assert(result.valid());
|
||||
assert(!result.complete());
|
||||
assert(!st.complete());
|
||||
|
||||
// Message type: row, error or eof?
|
||||
std::uint8_t msg_type = 0;
|
||||
|
@ -158,7 +157,7 @@ void boost::mysql::detail::deserialize_row(
|
|||
err = deserialize_message(ctx, ok_pack);
|
||||
if (err)
|
||||
return;
|
||||
result.complete(ok_pack);
|
||||
st.complete(ok_pack);
|
||||
}
|
||||
else if (msg_type == error_packet_header)
|
||||
{
|
||||
|
@ -169,7 +168,7 @@ void boost::mysql::detail::deserialize_row(
|
|||
{
|
||||
// An actual row
|
||||
ctx.rewind(1); // keep the 'message type' byte, as it is part of the actual message
|
||||
deserialize_row(result.encoding(), ctx, result.fields(), buffer_first, output, err);
|
||||
deserialize_row(st.encoding(), ctx, st.fields(), buffer_first, output, err);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ inline protocol_field_type get_protocol_field_type(const field_view& input) noex
|
|||
case field_kind::null: return protocol_field_type::null;
|
||||
case field_kind::int64: return protocol_field_type::longlong;
|
||||
case field_kind::uint64: return protocol_field_type::longlong;
|
||||
case field_kind::string: return protocol_field_type::varchar;
|
||||
case field_kind::string: return protocol_field_type::string;
|
||||
case field_kind::blob: return protocol_field_type::blob;
|
||||
case field_kind::float_: return protocol_field_type::float_;
|
||||
case field_kind::double_: return protocol_field_type::double_;
|
||||
|
@ -74,13 +74,18 @@ inline std::size_t boost::mysql::detail::serialization_traits<
|
|||
get_size(ctx, value.statement_id, value.flags, value.iteration_count);
|
||||
auto num_params = std::distance(value.params_begin, value.params_end);
|
||||
assert(num_params >= 0 && num_params <= 255);
|
||||
res += null_bitmap_traits(stmt_execute_null_bitmap_offset, num_params).byte_count();
|
||||
res += get_size(ctx, value.new_params_bind_flag);
|
||||
res += get_size(ctx, com_stmt_execute_param_meta_packet{}) * num_params;
|
||||
for (auto it = value.params_begin; it != value.params_end; ++it)
|
||||
|
||||
if (num_params > 0u)
|
||||
{
|
||||
res += get_size(ctx, *it);
|
||||
res += null_bitmap_traits(stmt_execute_null_bitmap_offset, num_params).byte_count();
|
||||
res += get_size(ctx, value.new_params_bind_flag);
|
||||
res += get_size(ctx, com_stmt_execute_param_meta_packet{}) * num_params;
|
||||
for (auto it = value.params_begin; it != value.params_end; ++it)
|
||||
{
|
||||
res += get_size(ctx, *it);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -100,35 +105,38 @@ inline void boost::mysql::detail::serialization_traits<
|
|||
auto num_params = std::distance(input.params_begin, input.params_end);
|
||||
assert(num_params >= 0 && num_params <= 255);
|
||||
|
||||
// NULL bitmap (already size zero if num_params == 0)
|
||||
null_bitmap_traits traits(stmt_execute_null_bitmap_offset, num_params);
|
||||
std::size_t i = 0;
|
||||
std::memset(ctx.first(), 0, traits.byte_count()); // Initialize to zeroes
|
||||
for (auto it = input.params_begin; it != input.params_end; ++it, ++i)
|
||||
if (num_params > 0)
|
||||
{
|
||||
if (it->is_null())
|
||||
// NULL bitmap
|
||||
null_bitmap_traits traits(stmt_execute_null_bitmap_offset, num_params);
|
||||
std::size_t i = 0;
|
||||
std::memset(ctx.first(), 0, traits.byte_count()); // Initialize to zeroes
|
||||
for (auto it = input.params_begin; it != input.params_end; ++it, ++i)
|
||||
{
|
||||
traits.set_null(ctx.first(), i);
|
||||
if (it->is_null())
|
||||
{
|
||||
traits.set_null(ctx.first(), i);
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.advance(traits.byte_count());
|
||||
ctx.advance(traits.byte_count());
|
||||
|
||||
// new parameters bind flag
|
||||
serialize(ctx, input.new_params_bind_flag);
|
||||
// new parameters bind flag
|
||||
serialize(ctx, input.new_params_bind_flag);
|
||||
|
||||
// value metadata
|
||||
com_stmt_execute_param_meta_packet meta;
|
||||
for (auto it = input.params_begin; it != input.params_end; ++it)
|
||||
{
|
||||
meta.type = get_protocol_field_type(*it);
|
||||
meta.unsigned_flag = is_unsigned(*it) ? 0x80 : 0;
|
||||
serialize(ctx, meta);
|
||||
}
|
||||
// value metadata
|
||||
com_stmt_execute_param_meta_packet meta;
|
||||
for (auto it = input.params_begin; it != input.params_end; ++it)
|
||||
{
|
||||
meta.type = get_protocol_field_type(*it);
|
||||
meta.unsigned_flag = is_unsigned(*it) ? 0x80 : 0;
|
||||
serialize(ctx, meta);
|
||||
}
|
||||
|
||||
// actual values
|
||||
for (auto it = input.params_begin; it != input.params_end; ++it)
|
||||
{
|
||||
serialize(ctx, *it);
|
||||
// actual values
|
||||
for (auto it = input.params_begin; it != input.params_end; ++it)
|
||||
{
|
||||
serialize(ctx, *it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1622,7 +1622,6 @@ enum class errc : int
|
|||
};
|
||||
|
||||
/**
|
||||
* \relates errc
|
||||
* \brief Streams an error code.
|
||||
*/
|
||||
inline std::ostream& operator<<(std::ostream&, errc);
|
||||
|
|
|
@ -26,9 +26,13 @@ inline error_code make_error_code(errc error);
|
|||
|
||||
/**
|
||||
* \brief Additional information about error conditions
|
||||
* \details Contains an error message describing what happened. Not all error
|
||||
* \details
|
||||
* Contains an error message describing what happened. Not all error
|
||||
* conditions are able to generate this extended information - those that
|
||||
* can't have an empty error message.
|
||||
*\n
|
||||
* \ref message is encoded using the connection's character set. It's generated
|
||||
* by the server, and may potentially contain user input.
|
||||
*/
|
||||
class error_info
|
||||
{
|
||||
|
@ -44,7 +48,7 @@ public:
|
|||
/// Gets the error message.
|
||||
const std::string& message() const noexcept { return msg_; }
|
||||
|
||||
/// Sets the error message.
|
||||
// TODO: hide this
|
||||
void set_message(std::string&& err) { msg_ = std::move(err); }
|
||||
|
||||
/// Restores the object to its initial state.
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_MYSQL_EXECUTE_OPTIONS_HPP
|
||||
#define BOOST_MYSQL_EXECUTE_OPTIONS_HPP
|
||||
|
||||
#include <boost/mysql/detail/auxiliar/field_type_traits.hpp>
|
||||
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
|
||||
/**
|
||||
* \brief Additional statement execution options.
|
||||
* \details Placeholder for now.
|
||||
*/
|
||||
class execute_options
|
||||
{
|
||||
public:
|
||||
};
|
||||
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
|
@ -0,0 +1,156 @@
|
|||
//
|
||||
// Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_MYSQL_EXECUTION_STATE_HPP
|
||||
#define BOOST_MYSQL_EXECUTION_STATE_HPP
|
||||
|
||||
#include <boost/mysql/metadata.hpp>
|
||||
#include <boost/mysql/metadata_collection_view.hpp>
|
||||
|
||||
#include <boost/mysql/detail/protocol/common_messages.hpp>
|
||||
#include <boost/mysql/detail/protocol/resultset_encoding.hpp>
|
||||
|
||||
#include <boost/utility/string_view.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
|
||||
/**
|
||||
* \brief Holds state for multi-function SQL execution operations.
|
||||
*/
|
||||
class execution_state
|
||||
{
|
||||
bool eof_received_{false};
|
||||
std::uint8_t seqnum_{};
|
||||
detail::resultset_encoding encoding_{detail::resultset_encoding::text};
|
||||
std::vector<metadata> meta_;
|
||||
std::uint64_t affected_rows_{};
|
||||
std::uint64_t last_insert_id_{};
|
||||
std::uint16_t warnings_{};
|
||||
std::vector<char> info_; // guarantee that no SBO is used
|
||||
|
||||
public:
|
||||
#ifndef BOOST_MYSQL_DOXYGEN
|
||||
// Private, do not use. TODO: hide these
|
||||
void reset(detail::resultset_encoding encoding) noexcept
|
||||
{
|
||||
seqnum_ = 0;
|
||||
encoding_ = encoding;
|
||||
meta_.clear();
|
||||
eof_received_ = false;
|
||||
}
|
||||
|
||||
void complete(const detail::ok_packet& pack)
|
||||
{
|
||||
affected_rows_ = pack.affected_rows.value;
|
||||
last_insert_id_ = pack.last_insert_id.value;
|
||||
warnings_ = pack.warnings;
|
||||
info_.assign(pack.info.value.begin(), pack.info.value.end());
|
||||
eof_received_ = true;
|
||||
}
|
||||
|
||||
void prepare_meta(std::size_t num_fields) { meta_.reserve(num_fields); }
|
||||
|
||||
void add_meta(const detail::column_definition_packet& pack) { meta_.emplace_back(pack, true); }
|
||||
|
||||
detail::resultset_encoding encoding() const noexcept { return encoding_; }
|
||||
|
||||
std::uint8_t& sequence_number() noexcept { return seqnum_; }
|
||||
|
||||
std::vector<metadata>& fields() noexcept { return meta_; }
|
||||
const std::vector<metadata>& fields() const noexcept { return meta_; }
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Default constructor.
|
||||
* \details The constructed object is guaranteed to have `meta().empty()` and
|
||||
* `!complete()`.
|
||||
*/
|
||||
execution_state() = default;
|
||||
|
||||
/**
|
||||
* \brief Returns whether the resultset generated by this operation has been completely read.
|
||||
* \details
|
||||
* Once `complete`, you may access extra information about the operation, like
|
||||
* \ref affected_rows or \ref last_insert_id.
|
||||
*/
|
||||
bool complete() const noexcept { return eof_received_; }
|
||||
|
||||
/**
|
||||
* \brief Returns metadata about the columns in the query.
|
||||
* \details
|
||||
* The returned collection will have as many \ref metadata objects as columns retrieved by
|
||||
* the SQL query, and in the same order.
|
||||
*\n
|
||||
* This function returns a view object, with reference semantics. The returned view points into
|
||||
* memory owned by `*this`, and will be valid as long as `*this` or an object move-constructed
|
||||
* from `*this` are alive.
|
||||
*/
|
||||
metadata_collection_view meta() const noexcept
|
||||
{
|
||||
return metadata_collection_view(meta_.data(), meta_.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Returns the number of rows affected by the executed SQL statement.
|
||||
* \details Precondition: `this->complete() == true`.
|
||||
*/
|
||||
std::uint64_t affected_rows() const noexcept
|
||||
{
|
||||
assert(complete());
|
||||
return affected_rows_;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Returns the last insert ID produced by the executed SQL statement.
|
||||
* \details Precondition: `this->complete() == true`.
|
||||
*/
|
||||
std::uint64_t last_insert_id() const noexcept
|
||||
{
|
||||
assert(complete());
|
||||
return last_insert_id_;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Returns the number of warnings produced by the executed SQL statement.
|
||||
* \details Precondition: `this->complete() == true`.
|
||||
*/
|
||||
unsigned warning_count() const noexcept
|
||||
{
|
||||
assert(complete());
|
||||
return warnings_;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Returns additionat text information about the execution of the SQL statement.
|
||||
* \details Precondition: `this->complete() == true`.
|
||||
*\n
|
||||
* The format of this information is documented by MySQL <a
|
||||
* href="https://dev.mysql.com/doc/c-api/8.0/en/mysql-info.html">here</a>.
|
||||
*\n
|
||||
* The returned string always uses ASCII encoding, regardless of the connection's character set.
|
||||
*\n
|
||||
* This function returns a view object, with reference semantics. The returned view points into
|
||||
* memory owned by `*this`, and will be valid as long as `*this` or an object move-constructed
|
||||
* from `*this` are alive.
|
||||
*/
|
||||
boost::string_view info() const noexcept
|
||||
{
|
||||
assert(complete());
|
||||
return boost::string_view(info_.data(), info_.size());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
|
@ -51,20 +51,28 @@ public:
|
|||
/// Copy constructor.
|
||||
field(const field&) = default;
|
||||
|
||||
/// Move constructor.
|
||||
field(field&&) = default;
|
||||
/**
|
||||
* \brief Move constructor.
|
||||
* \details All references into `other` are invalidated, including the ones obtained by calling
|
||||
* get_xxx, as_xxx and \ref field::operator field_view().
|
||||
*/
|
||||
field(field&& other) = default;
|
||||
|
||||
/**
|
||||
* \brief Copy assignment.
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions.
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions,
|
||||
* but not the ones obtained by \ref field::operator field_view().
|
||||
*/
|
||||
field& operator=(const field&) = default;
|
||||
|
||||
/**
|
||||
* \brief Move assignment.
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions.
|
||||
* \details Invalidates references to `*this` obtained by as_xxx and get_xxx functions,
|
||||
* but not the ones obtained by \ref field::operator field_view(). All references into `other`
|
||||
* are invalidated, including the ones obtained by calling get_xxx, as_xxx and
|
||||
* \ref field::operator field_view().
|
||||
*/
|
||||
field& operator=(field&&) = default;
|
||||
field& operator=(field&& other) = default;
|
||||
|
||||
/// Destructor.
|
||||
~field() = default;
|
||||
|
@ -158,11 +166,8 @@ public:
|
|||
field(const field_view& v) { from_view(v); }
|
||||
|
||||
/**
|
||||
* \brief Replaces `*this` with a `NULL`, changing the kind to `null` and destroying any
|
||||
* previous contents.
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions.
|
||||
*\n
|
||||
* Equivalent to \ref field::emplace_null.
|
||||
* \copydoc emplace_null
|
||||
* \details Equivalent to \ref emplace_null.
|
||||
*/
|
||||
field& operator=(std::nullptr_t) noexcept
|
||||
{
|
||||
|
@ -171,11 +176,8 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* \brief Replaces `*this` with `v`, changing the kind to `int64` and destroying any
|
||||
* previous contents.
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions.
|
||||
*\n
|
||||
* Equivalent to \ref field::emplace_int64.
|
||||
* \copydoc emplace_int64
|
||||
* \details Equivalent to \ref emplace_int64.
|
||||
*/
|
||||
field& operator=(signed char v) noexcept
|
||||
{
|
||||
|
@ -212,11 +214,8 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* \brief Replaces `*this` with `v`, changing the kind to `uint64` and destroying any
|
||||
* previous contents.
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions.
|
||||
*\n
|
||||
* Equivalent to \ref field::emplace_uint64.
|
||||
* \copydoc emplace_uint64
|
||||
* \details Equivalent to \ref emplace_uint64.
|
||||
*/
|
||||
field& operator=(unsigned char v) noexcept
|
||||
{
|
||||
|
@ -253,11 +252,8 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* \brief Replaces `*this` with `v`, changing the kind to `string` and destroying any previous
|
||||
* contents.
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions.
|
||||
*\n
|
||||
* Equivalent to \ref field::emplace_string.
|
||||
* \copydoc emplace_string
|
||||
* \details Equivalent to \ref emplace_string.
|
||||
*/
|
||||
field& operator=(const std::string& v)
|
||||
{
|
||||
|
@ -265,31 +261,21 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \copybrief operator=(const std::string&)
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions.
|
||||
*\n
|
||||
* Equivalent to \ref field::emplace_string.
|
||||
*/
|
||||
/// \copydoc operator=(const std::string&)
|
||||
field& operator=(std::string&& v)
|
||||
{
|
||||
emplace_string(std::move(v));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \copybrief operator=(const std::string&)
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions.
|
||||
*\n
|
||||
* Equivalent to \ref field::emplace_string.
|
||||
*/
|
||||
/// \copydoc operator=(const std::string&)
|
||||
field& operator=(const char* v)
|
||||
{
|
||||
emplace_string(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// \copydoc operator=(const char*)
|
||||
/// \copydoc operator=(const std::string&)
|
||||
field& operator=(boost::string_view v)
|
||||
{
|
||||
emplace_string(v);
|
||||
|
@ -297,7 +283,7 @@ public:
|
|||
}
|
||||
|
||||
#if defined(__cpp_lib_string_view) || defined(BOOST_MYSQL_DOXYGEN)
|
||||
/// \copydoc operator=(const char*)
|
||||
/// \copydoc operator=(const std::string&)
|
||||
field& operator=(std::string_view v)
|
||||
{
|
||||
emplace_string(v);
|
||||
|
@ -306,11 +292,8 @@ public:
|
|||
#endif
|
||||
|
||||
/**
|
||||
* \brief Replaces `*this` with `v`, changing the kind to `blob` and destroying any
|
||||
* previous contents.
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions.
|
||||
*\n
|
||||
* Equivalent to \ref field::emplace_blob.
|
||||
* \copydoc emplace_blob
|
||||
* \details Equivalent to \ref emplace_blob.
|
||||
*/
|
||||
field& operator=(blob v)
|
||||
{
|
||||
|
@ -319,11 +302,8 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* \brief Replaces `*this` with `v`, changing the kind to `float_` and destroying any
|
||||
* previous contents.
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions.
|
||||
*\n
|
||||
* Equivalent to \ref field::emplace_float.
|
||||
* \copydoc emplace_float
|
||||
* \details Equivalent to \ref emplace_float.
|
||||
*/
|
||||
field& operator=(float v) noexcept
|
||||
{
|
||||
|
@ -332,11 +312,8 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* \brief Replaces `*this` with `v`, changing the kind to `double` and destroying any
|
||||
* previous contents.
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions.
|
||||
*\n
|
||||
* Equivalent to \ref field::emplace_double.
|
||||
* \copydoc emplace_double
|
||||
* \details Equivalent to \ref emplace_double.
|
||||
*/
|
||||
field& operator=(double v) noexcept
|
||||
{
|
||||
|
@ -345,11 +322,8 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* \brief Replaces `*this` with `v`, changing the kind to `date` and destroying any
|
||||
* previous contents.
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions.
|
||||
*\n
|
||||
* Equivalent to \ref field::emplace_date.
|
||||
* \copydoc emplace_date
|
||||
* \details Equivalent to \ref emplace_date.
|
||||
*/
|
||||
field& operator=(const date& v) noexcept
|
||||
{
|
||||
|
@ -358,11 +332,8 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* \brief Replaces `*this` with `v`, changing the kind to `datetime` and destroying any
|
||||
* previous contents.
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions.
|
||||
*\n
|
||||
* Equivalent to \ref field::emplace_datetime.
|
||||
* \copydoc emplace_datetime
|
||||
* \details Equivalent to \ref emplace_datetime.
|
||||
*/
|
||||
field& operator=(const datetime& v) noexcept
|
||||
{
|
||||
|
@ -371,11 +342,8 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* \brief Replaces `*this` with `v`, changing the kind to `time` and destroying any
|
||||
* previous contents.
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions.
|
||||
*\n
|
||||
* Equivalent to \ref field::emplace_time.
|
||||
* \copydoc emplace_time
|
||||
* \details Equivalent to \ref emplace_time.
|
||||
*/
|
||||
field& operator=(const time& v) noexcept
|
||||
{
|
||||
|
@ -386,7 +354,8 @@ public:
|
|||
/**
|
||||
* \brief Replaces `*this` with `v`, changing the kind to `v.kind()` and destroying any previous
|
||||
* contents.
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions.
|
||||
* \details Invalidates references to `*this` obtained by as_xxx and get_xxx functions, but not
|
||||
* the ones obtained by \ref field::operator field_view().
|
||||
*\n
|
||||
* `*this` is guaranteed to be valid even after `v` becomes invalid.
|
||||
*/
|
||||
|
@ -646,26 +615,34 @@ public:
|
|||
time& get_time() noexcept { return repr_.get<time>(); }
|
||||
|
||||
/**
|
||||
* \copybrief operator=(std::nullptr_t)
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions.
|
||||
* \brief Replaces `*this` with a `NULL`, changing the kind to `null` and destroying any
|
||||
* previous contents.
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions,
|
||||
* but not the ones obtained by \ref field::operator field_view().
|
||||
*/
|
||||
void emplace_null() noexcept { repr_.data.emplace<detail::field_impl::null_t>(); }
|
||||
|
||||
/**
|
||||
* \copybrief operator=(long long)
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions.
|
||||
* \brief Replaces `*this` with `v`, changing the kind to `int64` and destroying any
|
||||
* previous contents.
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions,
|
||||
* but not the ones obtained by \ref field::operator field_view().
|
||||
*/
|
||||
void emplace_int64(std::int64_t v) noexcept { repr_.data.emplace<std::int64_t>(v); }
|
||||
|
||||
/**
|
||||
* \copybrief operator=(unsigned long long)
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions.
|
||||
* \brief Replaces `*this` with `v`, changing the kind to `uint64` and destroying any
|
||||
* previous contents.
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions,
|
||||
* but not the ones obtained by \ref field::operator field_view().
|
||||
*/
|
||||
void emplace_uint64(std::uint64_t v) noexcept { repr_.data.emplace<std::uint64_t>(v); }
|
||||
|
||||
/**
|
||||
* \copybrief operator=(const std::string&)
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions.
|
||||
* \brief Replaces `*this` with `v`, changing the kind to `string` and destroying any previous
|
||||
* contents.
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions,
|
||||
* but not the ones obtained by \ref field::operator field_view().
|
||||
*/
|
||||
void emplace_string(const std::string& v) { repr_.data.emplace<std::string>(v); }
|
||||
|
||||
|
@ -684,38 +661,49 @@ public:
|
|||
#endif
|
||||
|
||||
/**
|
||||
* \copybrief operator=(blob)
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions.
|
||||
* \brief Replaces `*this` with `v`, changing the kind to `blob` and destroying any
|
||||
* previous contents.
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions,
|
||||
* but not the ones obtained by \ref field::operator field_view().
|
||||
*/
|
||||
void emplace_blob(blob v) { repr_.data.emplace<blob>(std::move(v)); }
|
||||
|
||||
/**
|
||||
* \copybrief operator=(float)
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions.
|
||||
* \brief Replaces `*this` with `v`, changing the kind to `float_` and destroying any
|
||||
* previous contents.
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions,
|
||||
* but not the ones obtained by \ref field::operator field_view().
|
||||
*/
|
||||
void emplace_float(float v) noexcept { repr_.data.emplace<float>(v); }
|
||||
|
||||
/**
|
||||
* \copybrief operator=(double)
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions.
|
||||
* \brief Replaces `*this` with `v`, changing the kind to `double` and destroying any
|
||||
* previous contents.
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions,
|
||||
* but not the ones obtained by \ref field::operator field_view().
|
||||
*/
|
||||
void emplace_double(double v) noexcept { repr_.data.emplace<double>(v); }
|
||||
|
||||
/**
|
||||
* \copybrief operator=(const date&)
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions.
|
||||
* \brief Replaces `*this` with `v`, changing the kind to `date` and destroying any
|
||||
* previous contents.
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions,
|
||||
* but not the ones obtained by \ref field::operator field_view().
|
||||
*/
|
||||
void emplace_date(const date& v) noexcept { repr_.data.emplace<date>(v); }
|
||||
|
||||
/**
|
||||
* \copybrief operator=(const datetime&)
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions.
|
||||
* \brief Replaces `*this` with `v`, changing the kind to `datetime` and destroying any
|
||||
* previous contents.
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions,
|
||||
* but not the ones obtained by \ref field::operator field_view().
|
||||
*/
|
||||
void emplace_datetime(const datetime& v) noexcept { repr_.data.emplace<datetime>(v); }
|
||||
|
||||
/**
|
||||
* \copybrief operator=(const time&)
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions.
|
||||
* \brief Replaces `*this` with `v`, changing the kind to `time` and destroying any
|
||||
* previous contents.
|
||||
* \details Invalidates references obtained by as_xxx and get_xxx functions, but not
|
||||
*/
|
||||
void emplace_time(const time& v) noexcept { repr_.data.emplace<time>(v); }
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace boost {
|
|||
namespace mysql {
|
||||
|
||||
/**
|
||||
* \brief Represents the possible C++ types a \ref field or \ref field_view may have.
|
||||
* \brief Represents the possible C++ types a `field` or `field_view` may have.
|
||||
*/
|
||||
enum class field_kind
|
||||
{
|
||||
|
@ -54,7 +54,6 @@ enum class field_kind
|
|||
};
|
||||
|
||||
/**
|
||||
* \relates field_kind
|
||||
* \brief Streams a field_kind.
|
||||
*/
|
||||
inline std::ostream& operator<<(std::ostream& os, field_kind v);
|
||||
|
|
|
@ -11,16 +11,20 @@
|
|||
#pragma once
|
||||
|
||||
#include <boost/mysql/connection.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/close_connection.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/connect.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/execute_query.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/handshake.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/prepare_statement.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/quit_connection.hpp>
|
||||
#include <boost/mysql/resultset_base.hpp>
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
#include <boost/mysql/row.hpp>
|
||||
#include <boost/mysql/statement_base.hpp>
|
||||
|
||||
#include <boost/mysql/detail/network_algorithms/close_connection.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/connect.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/handshake.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/prepare_statement.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/query.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/quit_connection.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/read_one_row.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/read_some_rows.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/start_query.hpp>
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
|
||||
#include <utility>
|
||||
|
@ -109,27 +113,65 @@ boost::mysql::connection<Stream>::async_handshake(
|
|||
);
|
||||
}
|
||||
|
||||
// Query
|
||||
template <class Stream>
|
||||
void boost::mysql::connection<Stream>::query(
|
||||
void boost::mysql::connection<Stream>::start_query(
|
||||
boost::string_view query_string,
|
||||
resultset<Stream>& result,
|
||||
execution_state& result,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
detail::clear_errors(err, info);
|
||||
detail::execute_query(get_channel(), query_string, result, err, info);
|
||||
detail::start_query(get_channel(), query_string, result, err, info);
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
void boost::mysql::connection<Stream>::start_query(
|
||||
boost::string_view query_string,
|
||||
execution_state& result
|
||||
)
|
||||
{
|
||||
detail::error_block blk;
|
||||
detail::start_query(get_channel(), query_string, result, blk.err, blk.info);
|
||||
blk.check();
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
||||
boost::mysql::connection<Stream>::async_start_query(
|
||||
boost::string_view query_string,
|
||||
execution_state& result,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
return detail::async_start_query(
|
||||
get_channel(),
|
||||
query_string,
|
||||
result,
|
||||
output_info,
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
void boost::mysql::connection<Stream>::query(
|
||||
boost::string_view query_string,
|
||||
resultset<Stream>& result
|
||||
resultset& result,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
detail::clear_errors(err, info);
|
||||
detail::query(get_channel(), query_string, result, err, info);
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
void boost::mysql::connection<Stream>::query(boost::string_view query_string, resultset& result)
|
||||
{
|
||||
detail::error_block blk;
|
||||
detail::execute_query(get_channel(), query_string, result, blk.err, blk.info);
|
||||
detail::query(get_channel(), query_string, result, blk.err, blk.info);
|
||||
blk.check();
|
||||
}
|
||||
|
||||
|
@ -138,12 +180,12 @@ template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) Comp
|
|||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
||||
boost::mysql::connection<Stream>::async_query(
|
||||
boost::string_view query_string,
|
||||
resultset<Stream>& result,
|
||||
resultset& result,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
return detail::async_execute_query(
|
||||
return detail::async_query(
|
||||
get_channel(),
|
||||
query_string,
|
||||
result,
|
||||
|
@ -250,4 +292,87 @@ boost::mysql::connection<Stream>::async_quit(error_info& output_info, Completion
|
|||
);
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
boost::mysql::row_view boost::mysql::connection<Stream>::read_one_row(
|
||||
execution_state& st,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
detail::clear_errors(err, info);
|
||||
return detail::read_one_row(get_channel(), st, err, info);
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
boost::mysql::row_view boost::mysql::connection<Stream>::read_one_row(execution_state& st)
|
||||
{
|
||||
detail::error_block blk;
|
||||
row_view res = detail::read_one_row(get_channel(), st, blk.err, blk.info);
|
||||
blk.check();
|
||||
return res;
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, ::boost::mysql::row_view)
|
||||
) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
|
||||
CompletionToken,
|
||||
void(boost::mysql::error_code, boost::mysql::row_view)
|
||||
)
|
||||
boost::mysql::connection<Stream>::async_read_one_row(
|
||||
execution_state& st,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
return detail::async_read_one_row(
|
||||
get_channel(),
|
||||
st,
|
||||
output_info,
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
boost::mysql::rows_view boost::mysql::connection<Stream>::read_some_rows(
|
||||
execution_state& st,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
detail::clear_errors(err, info);
|
||||
return detail::read_some_rows(get_channel(), st, err, info);
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
boost::mysql::rows_view boost::mysql::connection<Stream>::read_some_rows(execution_state& st)
|
||||
{
|
||||
detail::error_block blk;
|
||||
rows_view res = detail::read_some_rows(get_channel(), st, blk.err, blk.info);
|
||||
blk.check();
|
||||
return res;
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
template <
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, ::boost::mysql::rows_view))
|
||||
CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
|
||||
CompletionToken,
|
||||
void(boost::mysql::error_code, boost::mysql::rows_view)
|
||||
)
|
||||
boost::mysql::connection<Stream>::async_read_some_rows(
|
||||
execution_state& st,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
return detail::async_read_some_rows(
|
||||
get_channel(),
|
||||
st,
|
||||
output_info,
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,246 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_MYSQL_IMPL_RESULTSET_HPP
|
||||
#define BOOST_MYSQL_IMPL_RESULTSET_HPP
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/mysql/detail/network_algorithms/read_all_rows.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/read_one_row.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/read_some_rows.hpp>
|
||||
#include <boost/mysql/resultset.hpp>
|
||||
|
||||
// Read one row
|
||||
template <class Stream>
|
||||
boost::mysql::row_view boost::mysql::resultset<Stream>::read_one(
|
||||
use_views_t,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
detail::clear_errors(err, info);
|
||||
return detail::read_one_row(get_channel(), *this, err, info);
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
boost::mysql::row_view boost::mysql::resultset<Stream>::read_one(use_views_t)
|
||||
{
|
||||
detail::error_block blk;
|
||||
row_view res = detail::read_one_row(get_channel(), *this, blk.err, blk.info);
|
||||
blk.check();
|
||||
return res;
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, ::boost::mysql::row_view)
|
||||
) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
|
||||
CompletionToken,
|
||||
void(boost::mysql::error_code, boost::mysql::row_view)
|
||||
)
|
||||
boost::mysql::resultset<Stream>::async_read_one(
|
||||
use_views_t,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
return detail::async_read_one_row(
|
||||
get_channel(),
|
||||
*this,
|
||||
output_info,
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
bool boost::mysql::resultset<Stream>::read_one(row& output, error_code& err, error_info& info)
|
||||
{
|
||||
detail::clear_errors(err, info);
|
||||
return detail::read_one_row(get_channel(), *this, output, err, info);
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
bool boost::mysql::resultset<Stream>::read_one(row& output)
|
||||
{
|
||||
detail::error_block blk;
|
||||
bool res = detail::read_one_row(get_channel(), *this, output, blk.err, blk.info);
|
||||
blk.check();
|
||||
return res;
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, bool)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code, bool))
|
||||
boost::mysql::resultset<Stream>::async_read_one(
|
||||
row& output,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
return detail::async_read_one_row(
|
||||
get_channel(),
|
||||
*this,
|
||||
output,
|
||||
output_info,
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
}
|
||||
|
||||
// Read some rows
|
||||
template <class Stream>
|
||||
boost::mysql::rows_view boost::mysql::resultset<Stream>::read_some(
|
||||
use_views_t,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
detail::clear_errors(err, info);
|
||||
return detail::read_some_rows(get_channel(), *this, err, info);
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
boost::mysql::rows_view boost::mysql::resultset<Stream>::read_some(use_views_t)
|
||||
{
|
||||
detail::error_block blk;
|
||||
rows_view res = detail::read_some_rows(get_channel(), *this, blk.err, blk.info);
|
||||
blk.check();
|
||||
return res;
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
template <
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, ::boost::mysql::rows_view))
|
||||
CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
|
||||
CompletionToken,
|
||||
void(boost::mysql::error_code, boost::mysql::rows_view)
|
||||
)
|
||||
boost::mysql::resultset<Stream>::async_read_some(
|
||||
use_views_t,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
return detail::async_read_some_rows(
|
||||
get_channel(),
|
||||
*this,
|
||||
output_info,
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
void boost::mysql::resultset<Stream>::read_some(rows& output, error_code& err, error_info& info)
|
||||
{
|
||||
detail::clear_errors(err, info);
|
||||
detail::read_some_rows(get_channel(), *this, output, err, info);
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
void boost::mysql::resultset<Stream>::read_some(rows& output)
|
||||
{
|
||||
detail::error_block blk;
|
||||
detail::read_some_rows(get_channel(), *this, output, blk.err, blk.info);
|
||||
blk.check();
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
||||
boost::mysql::resultset<Stream>::async_read_some(
|
||||
rows& output,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
return detail::async_read_some_rows(
|
||||
get_channel(),
|
||||
*this,
|
||||
output,
|
||||
output_info,
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
}
|
||||
|
||||
// Read all rows
|
||||
template <class Stream>
|
||||
boost::mysql::rows_view boost::mysql::resultset<Stream>::read_all(
|
||||
use_views_t,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
detail::clear_errors(err, info);
|
||||
return detail::read_all_rows(get_channel(), *this, err, info);
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
boost::mysql::rows_view boost::mysql::resultset<Stream>::read_all(use_views_t)
|
||||
{
|
||||
detail::error_block blk;
|
||||
rows_view res = detail::read_all_rows(get_channel(), *this, blk.err, blk.info);
|
||||
blk.check();
|
||||
return res;
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
template <
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, ::boost::mysql::rows_view))
|
||||
CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
|
||||
CompletionToken,
|
||||
void(boost::mysql::error_code, boost::mysql::rows_view)
|
||||
)
|
||||
boost::mysql::resultset<Stream>::async_read_all(
|
||||
use_views_t,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
return detail::async_read_all_rows(
|
||||
get_channel(),
|
||||
*this,
|
||||
output_info,
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
void boost::mysql::resultset<Stream>::read_all(rows& output, error_code& err, error_info& info)
|
||||
{
|
||||
detail::clear_errors(err, info);
|
||||
detail::read_all_rows(get_channel(), *this, output, err, info);
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
void boost::mysql::resultset<Stream>::read_all(rows& output)
|
||||
{
|
||||
detail::error_block blk;
|
||||
detail::read_all_rows(get_channel(), *this, output, blk.err, blk.info);
|
||||
blk.check();
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
||||
boost::mysql::resultset<Stream>::async_read_all(
|
||||
rows& output,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
return detail::async_read_all_rows(
|
||||
get_channel(),
|
||||
*this,
|
||||
output,
|
||||
output_info,
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -10,10 +10,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <boost/mysql/detail/auxiliar/rows_iterator.hpp>
|
||||
#include <boost/mysql/rows.hpp>
|
||||
#include <boost/mysql/rows_view.hpp>
|
||||
|
||||
#include <boost/mysql/detail/auxiliar/rows_iterator.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <stdexcept>
|
||||
|
@ -30,13 +31,12 @@ boost::mysql::rows& boost::mysql::rows::operator=(const rows_view& rhs)
|
|||
if (rhs.fields_ == fields_.data())
|
||||
{
|
||||
assert(rhs.num_fields_ == fields_.size());
|
||||
assert(rhs.num_columns() == num_columns());
|
||||
}
|
||||
else
|
||||
{
|
||||
assign(rhs.fields_, rhs.num_fields_);
|
||||
num_columns_ = rhs.num_columns_;
|
||||
}
|
||||
num_columns_ = rhs.num_columns_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,35 +10,31 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <boost/mysql/detail/network_algorithms/close_statement.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/execute_statement.hpp>
|
||||
#include <boost/mysql/statement.hpp>
|
||||
|
||||
// Execute statement, with tuple
|
||||
#include <boost/mysql/detail/network_algorithms/close_statement.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/execute_statement.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/start_statement_execution.hpp>
|
||||
|
||||
template <class Stream>
|
||||
template <BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple, class>
|
||||
void boost::mysql::statement<Stream>::execute(
|
||||
const FieldLikeTuple& params,
|
||||
const execute_options& opts,
|
||||
resultset<Stream>& result,
|
||||
resultset& result,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
detail::clear_errors(err, info);
|
||||
detail::execute_statement(get_channel(), *this, params, opts, result, err, info);
|
||||
detail::execute_statement(get_channel(), *this, params, result, err, info);
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
template <BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple, class>
|
||||
void boost::mysql::statement<Stream>::execute(
|
||||
const FieldLikeTuple& params,
|
||||
const execute_options& opts,
|
||||
resultset<Stream>& result
|
||||
)
|
||||
void boost::mysql::statement<Stream>::execute(const FieldLikeTuple& params, resultset& result)
|
||||
{
|
||||
detail::error_block blk;
|
||||
detail::execute_statement(get_channel(), *this, params, opts, result, blk.err, blk.info);
|
||||
detail::execute_statement(get_channel(), *this, params, result, blk.err, blk.info);
|
||||
blk.check();
|
||||
}
|
||||
|
||||
|
@ -50,8 +46,7 @@ template <
|
|||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
||||
boost::mysql::statement<Stream>::async_execute(
|
||||
FieldLikeTuple&& params,
|
||||
const execute_options& opts,
|
||||
resultset<Stream>& result,
|
||||
resultset& result,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
|
@ -60,7 +55,54 @@ boost::mysql::statement<Stream>::async_execute(
|
|||
get_channel(),
|
||||
*this,
|
||||
std::forward<FieldLikeTuple>(params),
|
||||
opts,
|
||||
result,
|
||||
output_info,
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
template <BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple, class>
|
||||
void boost::mysql::statement<Stream>::start_execution(
|
||||
const FieldLikeTuple& params,
|
||||
execution_state& result,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
detail::clear_errors(err, info);
|
||||
detail::start_statement_execution(get_channel(), *this, params, result, err, info);
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
template <BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple, class>
|
||||
void boost::mysql::statement<Stream>::start_execution(
|
||||
const FieldLikeTuple& params,
|
||||
execution_state& result
|
||||
)
|
||||
{
|
||||
detail::error_block blk;
|
||||
detail::start_statement_execution(get_channel(), *this, params, result, blk.err, blk.info);
|
||||
blk.check();
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
template <
|
||||
BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken,
|
||||
class>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
||||
boost::mysql::statement<Stream>::async_start_execution(
|
||||
FieldLikeTuple&& params,
|
||||
execution_state& result,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
return detail::async_start_statement_execution(
|
||||
get_channel(),
|
||||
*this,
|
||||
std::forward<FieldLikeTuple>(params),
|
||||
result,
|
||||
output_info,
|
||||
std::forward<CompletionToken>(token)
|
||||
|
@ -70,22 +112,20 @@ boost::mysql::statement<Stream>::async_execute(
|
|||
// Execute statement, with iterators
|
||||
template <class Stream>
|
||||
template <BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator>
|
||||
void boost::mysql::statement<Stream>::execute(
|
||||
void boost::mysql::statement<Stream>::start_execution(
|
||||
FieldViewFwdIterator params_first,
|
||||
FieldViewFwdIterator params_last,
|
||||
const execute_options& opts,
|
||||
resultset<Stream>& result,
|
||||
execution_state& result,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
detail::clear_errors(err, info);
|
||||
detail::execute_statement(
|
||||
detail::start_statement_execution(
|
||||
get_channel(),
|
||||
*this,
|
||||
params_first,
|
||||
params_last,
|
||||
opts,
|
||||
result,
|
||||
err,
|
||||
info
|
||||
|
@ -94,20 +134,18 @@ void boost::mysql::statement<Stream>::execute(
|
|||
|
||||
template <class Stream>
|
||||
template <BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator>
|
||||
void boost::mysql::statement<Stream>::execute(
|
||||
void boost::mysql::statement<Stream>::start_execution(
|
||||
FieldViewFwdIterator params_first,
|
||||
FieldViewFwdIterator params_last,
|
||||
const execute_options& opts,
|
||||
resultset<Stream>& result
|
||||
execution_state& result
|
||||
)
|
||||
{
|
||||
detail::error_block blk;
|
||||
detail::execute_statement(
|
||||
detail::start_statement_execution(
|
||||
get_channel(),
|
||||
*this,
|
||||
params_first,
|
||||
params_last,
|
||||
opts,
|
||||
result,
|
||||
blk.err,
|
||||
blk.info
|
||||
|
@ -120,21 +158,19 @@ template <
|
|||
BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
||||
boost::mysql::statement<Stream>::async_execute(
|
||||
boost::mysql::statement<Stream>::async_start_execution(
|
||||
FieldViewFwdIterator params_first,
|
||||
FieldViewFwdIterator params_last,
|
||||
const execute_options& opts,
|
||||
resultset<Stream>& result,
|
||||
execution_state& result,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
return detail::async_execute_statement(
|
||||
return detail::async_start_statement_execution(
|
||||
get_channel(),
|
||||
*this,
|
||||
params_first,
|
||||
params_last,
|
||||
opts,
|
||||
result,
|
||||
output_info,
|
||||
std::forward<CompletionToken>(token)
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#define BOOST_MYSQL_METADATA_HPP
|
||||
|
||||
#include <boost/mysql/column_type.hpp>
|
||||
|
||||
#include <boost/mysql/detail/auxiliar/bytestring.hpp>
|
||||
#include <boost/mysql/detail/protocol/common_messages.hpp>
|
||||
|
||||
|
@ -50,7 +51,7 @@ public:
|
|||
|
||||
/**
|
||||
* \brief Move constructor.
|
||||
* \details Any `string_view`s obtained by calling accessor functions on `other` remain valid.
|
||||
* \details `string_view`s obtained by calling accessor functions on `other` are invalidated.
|
||||
*/
|
||||
metadata(metadata&& other) = default;
|
||||
|
||||
|
@ -59,15 +60,15 @@ public:
|
|||
|
||||
/**
|
||||
* \brief Move assignment.
|
||||
* \details Any `string_view`s obtained by calling accessor functions on `other` remain valid.
|
||||
* Any `string_view`s pointing into `*this` are invalidated.
|
||||
* \details `string_view`s obtained by calling accessor functions on both `*this` and `other`
|
||||
* are invalidated.
|
||||
*/
|
||||
metadata& operator=(metadata&& other) = default;
|
||||
|
||||
/**
|
||||
* \brief Copy assignment.
|
||||
* \details Any `string_view`s obtained by calling accessor functions on `*this` are
|
||||
* invalidated.
|
||||
* \details `string_view`s obtained by calling accessor functions on `*this`
|
||||
* are invalidated.
|
||||
*/
|
||||
metadata& operator=(const metadata& other) = default;
|
||||
|
||||
|
@ -124,7 +125,12 @@ public:
|
|||
*/
|
||||
boost::string_view original_column_name() const noexcept { return org_name_; }
|
||||
|
||||
/// Returns the \ref collation of the column.
|
||||
/**
|
||||
* \brief Returns the collation that fields belonging to this column use.
|
||||
* \details This collation matches the character set and collation that
|
||||
* fields belonging to this column use, rather than the collation used to
|
||||
* define the column. It will almost always match the connection's collation.
|
||||
*/
|
||||
collation column_collation() const noexcept { return character_set_; }
|
||||
|
||||
/// Returns the maximum length of the column.
|
||||
|
|
|
@ -8,14 +8,10 @@
|
|||
#ifndef BOOST_MYSQL_RESULTSET_HPP
|
||||
#define BOOST_MYSQL_RESULTSET_HPP
|
||||
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
#include <boost/mysql/resultset_base.hpp>
|
||||
#include <boost/mysql/row_view.hpp>
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
#include <boost/mysql/metadata_collection_view.hpp>
|
||||
#include <boost/mysql/rows.hpp>
|
||||
#include <boost/mysql/rows_view.hpp>
|
||||
#include <boost/mysql/use_views.hpp>
|
||||
|
||||
#include <boost/asio/async_result.hpp>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
|
@ -23,378 +19,143 @@ namespace boost {
|
|||
namespace mysql {
|
||||
|
||||
/**
|
||||
* \brief Holds state and data about a query or statement execution, and allows reading the produced
|
||||
* rows.
|
||||
* \details
|
||||
* Resultsets are obtained as a result of a SQL statement execution, by calling \ref
|
||||
* connection::query or \ref statement::execute. A resultset holds metadata about the operation
|
||||
* (\ref meta), allows you to read the resulting rows ( \ref read_one, \ref read_some, \ref
|
||||
* read_all), and stores additional information about the execution ("EOF packet data", in MySQL
|
||||
* slang: \ref affected_rows, \ref last_insert_id, and so on). Resultsets are the glue that join
|
||||
* the different pieces of \ref connection::query and \ref statement::execute multi-function
|
||||
* operations.
|
||||
* \n
|
||||
* Resultsets are proxy I/O objects, meaning that they hold a reference to the internal state of the
|
||||
* \ref connection that created them. I/O operations on a resultset result in reads and writes on
|
||||
* the connection's stream. A `resultset` is usable for I/O operations as long as the original \ref
|
||||
* connection is alive and open. Moving the `connection` object doesn't invalidate `resultset`s
|
||||
* pointing into it, but destroying or closing it does. The executor object used by a `resultset` is
|
||||
* always the underlying stream's.
|
||||
* \n
|
||||
* Resultsets are default-constructible and movable, but not copyable. A default constructed or
|
||||
* closed resultset has `!this->valid()`. Calling any member function on an invalid
|
||||
* resultset, other than assignment, results in undefined behavior.
|
||||
* \brief Holds the results of a SQL query.
|
||||
*/
|
||||
template <class Stream>
|
||||
class resultset : public resultset_base
|
||||
class resultset
|
||||
{
|
||||
public:
|
||||
// TODO: hide these
|
||||
execution_state& state() noexcept { return st_; }
|
||||
::boost::mysql::rows& mutable_rows() noexcept { return rows_; }
|
||||
|
||||
/**
|
||||
* \brief Default constructor.
|
||||
* \details Default constructed resultsets have `!this->valid()`.
|
||||
* \details Constructs an empty resultset, with `this->has_value() == false`.
|
||||
*/
|
||||
resultset() = default;
|
||||
|
||||
#ifndef BOOST_MYSQL_DOXYGEN
|
||||
resultset(const resultset&) = delete;
|
||||
resultset& operator=(const resultset&) = delete;
|
||||
#endif
|
||||
/**
|
||||
* \brief Copy constructor.
|
||||
*/
|
||||
resultset(const resultset& other) = default;
|
||||
|
||||
/**
|
||||
* \brief Move constructor.
|
||||
* \details Views obtained from `other.meta()` and `other.info()` remain valid.
|
||||
* \details View objects referencing `other` remain valid.
|
||||
*/
|
||||
resultset(resultset&& other) noexcept : resultset_base(std::move(other)) { other.reset(); }
|
||||
resultset(resultset&& other) = default;
|
||||
|
||||
/**
|
||||
* \brief Copy assignment.
|
||||
* \details View objects referencing `*this` are invalidated.
|
||||
*/
|
||||
resultset& operator=(const resultset& other) = default;
|
||||
|
||||
/**
|
||||
* \brief Move assignment.
|
||||
* \details Views obtained from `other.meta()` and `other.info()` remain valid. Views obtained
|
||||
* from `this->meta()` and `this->info()` are invalidated.
|
||||
* \details View objects referencing `other` remain valid. View objects
|
||||
* referencing `*this` are invalidated.
|
||||
*/
|
||||
resultset& operator=(resultset&& other) noexcept
|
||||
{
|
||||
swap(other);
|
||||
other.reset();
|
||||
return *this;
|
||||
}
|
||||
resultset& operator=(resultset&& other) = default;
|
||||
|
||||
/**
|
||||
* \brief Destructor.
|
||||
* \details Views obtained from `this->meta()` and `this->info()` are invalidated.
|
||||
*/
|
||||
/// Destructor
|
||||
~resultset() = default;
|
||||
|
||||
/// The executor type associated to this object.
|
||||
using executor_type = typename Stream::executor_type;
|
||||
|
||||
/// Retrieves the executor associated to this object.
|
||||
executor_type get_executor() { return get_channel().get_executor(); }
|
||||
/**
|
||||
* \brief Returns whether the object holds a valid result.
|
||||
* \details Having `this->has_value()` is a precondition to call all data accessors.
|
||||
* Objects populated by \ref connection::query, \ref statement::execute or their async
|
||||
* counterparts are guaranteed to have `this->has_value()`.
|
||||
*/
|
||||
bool has_value() const noexcept { return st_.complete(); }
|
||||
|
||||
/**
|
||||
* \brief Reads a single row.
|
||||
* \details
|
||||
* Returns `true` if a row was read successfully. If there were no more rows to read,
|
||||
* it returns `false` and sets `output` to an empty row.
|
||||
* \brief Returns the rows retrieved by the SQL query.
|
||||
* \details Precondition: `this->has_value()`.
|
||||
*\n
|
||||
* Use this operation to read large resultsets that may not entirely fit in memory, or when
|
||||
* individual row processing is preferred.
|
||||
* This function returns a view object, with reference semantics. The returned view points into
|
||||
* memory owned by `*this`, and will be valid as long as `*this` or an object move-constructed
|
||||
* from `*this` are alive.
|
||||
*/
|
||||
bool read_one(row& output, error_code& err, error_info& info);
|
||||
|
||||
/// \copydoc read_one(row&,error_code&,error_info&)
|
||||
bool read_one(row& output);
|
||||
|
||||
/**
|
||||
* \copydoc read_one(row&,error_code&,error_info&)
|
||||
* \details
|
||||
* The handler signature for this operation is
|
||||
* `void(boost::mysql::error_code, bool)`.
|
||||
*/
|
||||
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, bool))
|
||||
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, bool))
|
||||
async_read_one(
|
||||
row& output,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
)
|
||||
rows_view rows() const noexcept
|
||||
{
|
||||
return async_read_one(
|
||||
output,
|
||||
get_channel().shared_info(),
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
assert(has_value());
|
||||
return rows_;
|
||||
}
|
||||
|
||||
/// \copydoc async_read_one(row&,CompletionToken&&)
|
||||
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, bool))
|
||||
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, bool))
|
||||
async_read_one(
|
||||
row& output,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
);
|
||||
|
||||
/**
|
||||
* \brief Reads a single row as a \ref row_view.
|
||||
* \brief Returns metadata about the columns in the query.
|
||||
* \details
|
||||
* If a row was read successfully, returns a non-empty \ref row_view.
|
||||
* If there were no more rows to read, returns an empty `row_view`.
|
||||
* Precondition: `this->has_value()`.
|
||||
*\n
|
||||
* The returned view points into the \ref connection that `*this` references.
|
||||
* It will be valid until the `connection` performs any other read operation
|
||||
* or is destroyed.
|
||||
* The returned collection will have as many \ref metadata objects as columns retrieved by
|
||||
* the SQL query, and in the same order.
|
||||
*\n
|
||||
* This function returns a view object, with reference semantics. The returned view points into
|
||||
* memory owned by `*this`, and will be valid as long as `*this` or an object move-constructed
|
||||
* from `*this` are alive.
|
||||
*/
|
||||
row_view read_one(use_views_t, error_code& err, error_info& info);
|
||||
|
||||
/// \copydoc read_one(use_views_t,error_code&,error_info&)
|
||||
row_view read_one(use_views_t);
|
||||
|
||||
/**
|
||||
* \copydoc read_one(use_views_t,error_code&,error_info&)
|
||||
*
|
||||
* The handler signature for this operation is
|
||||
* `void(boost::mysql::error_code, boost::mysql::row_view)`.
|
||||
*/
|
||||
template <
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, ::boost::mysql::row_view))
|
||||
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, row_view))
|
||||
async_read_one(
|
||||
use_views_t,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
)
|
||||
metadata_collection_view meta() const noexcept
|
||||
{
|
||||
return async_read_one(
|
||||
use_views,
|
||||
get_channel().shared_info(),
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
assert(has_value());
|
||||
return st_.meta();
|
||||
}
|
||||
|
||||
/// \copydoc async_read_one(use_views_t,CompletionToken&&)
|
||||
template <
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, ::boost::mysql::row_view))
|
||||
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, row_view))
|
||||
async_read_one(
|
||||
use_views_t,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
);
|
||||
|
||||
/**
|
||||
* \brief Reads a batch of rows.
|
||||
* \details
|
||||
* The number of rows that will be read is unspecified. The contents of `output` will be
|
||||
* replaced by the read rows, so you can obtain the number of read rows using `output.size()`.
|
||||
* If this resultset has still rows to read, at least one will be read. If there are no more
|
||||
* rows to be read, `output` will be `empty()`.
|
||||
* \n
|
||||
* The number of rows that will be read depends on the input buffer size. The bigger the buffer,
|
||||
* the greater the batch size (up to a maximum). You can set the initial buffer size in \ref
|
||||
* connection::connect, using \ref buffer_params::initial_read_buffer_size. The buffer may be
|
||||
* grown bigger if required by other read operations.
|
||||
* \n
|
||||
* This is the most complex but most performant way of reading rows. You may consider
|
||||
* the overload returning a \ref rows_view object, too, which performs less copying.
|
||||
* \brief Returns the number of rows affected by the executed SQL statement.
|
||||
* \details Precondition: `this->has_value() == true`.
|
||||
*/
|
||||
void read_some(rows& output, error_code& err, error_info& info);
|
||||
|
||||
/// \copydoc read_some(rows&,error_code&,error_info&)
|
||||
void read_some(rows& output);
|
||||
|
||||
/**
|
||||
* \copydoc read_some(rows&,error_code&,error_info&)
|
||||
* \details
|
||||
* The handler signature for this operation is `void(boost::mysql::error_code)`.
|
||||
*/
|
||||
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code))
|
||||
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_read_some(
|
||||
rows& output,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
)
|
||||
std::uint64_t affected_rows() const noexcept
|
||||
{
|
||||
return async_read_some(
|
||||
output,
|
||||
get_channel().shared_info(),
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
assert(has_value());
|
||||
return st_.affected_rows();
|
||||
}
|
||||
|
||||
/// \copydoc async_read_some(rows&,CompletionToken&&)
|
||||
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code))
|
||||
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_read_some(
|
||||
rows& output,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
);
|
||||
|
||||
/**
|
||||
* \brief Reads a batch of rows as a \ref rows_view.
|
||||
* \details
|
||||
* The number of rows that will be read is unspecified. The contents of `output` will be
|
||||
* replaced by the read rows, so you can obtain the number of read rows using `output.size()`.
|
||||
* If this resultset has still rows to read, at least one will be read. If there are no more
|
||||
* rows to be read, `output` will be `empty()`.
|
||||
* \n
|
||||
* The number of rows that will be read depends on the input buffer size. The bigger the buffer,
|
||||
* the greater the batch size (up to a maximum). You can set the initial buffer size in \ref
|
||||
* connection::connect, using \ref buffer_params::initial_read_buffer_size. The buffer may be
|
||||
* grown bigger if required by other read operations.
|
||||
* \n
|
||||
* The returned view points into the \ref connection that `*this` references.
|
||||
* It will be valid until the `connection` performs any other read operation
|
||||
* or is destroyed.
|
||||
* \n
|
||||
* This is the most complex but most performant way of reading rows.
|
||||
* \brief Returns the last insert ID produced by the executed SQL statement.
|
||||
* \details Precondition: `this->has_value() == true`.
|
||||
*/
|
||||
rows_view read_some(use_views_t, error_code& err, error_info& info);
|
||||
|
||||
/// \copydoc read_some(use_views_t,error_code&,error_info&)
|
||||
rows_view read_some(use_views_t);
|
||||
|
||||
/**
|
||||
* \copydoc read_some(use_views_t,error_code&,error_info&)
|
||||
* \details
|
||||
* The handler signature for this operation is
|
||||
* `void(boost::mysql::error_code, boost::mysql::rows_view)`.
|
||||
*/
|
||||
template <
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, ::boost::mysql::rows_view))
|
||||
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, rows_view))
|
||||
async_read_some(
|
||||
use_views_t,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
)
|
||||
std::uint64_t last_insert_id() const noexcept
|
||||
{
|
||||
return async_read_some(
|
||||
use_views,
|
||||
get_channel().shared_info(),
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
assert(has_value());
|
||||
return st_.last_insert_id();
|
||||
}
|
||||
|
||||
/// \copydoc async_read_some(use_views_t,CompletionToken&&)
|
||||
template <
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, ::boost::mysql::rows_view))
|
||||
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, rows_view))
|
||||
async_read_some(
|
||||
use_views_t,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
);
|
||||
|
||||
/**
|
||||
* \brief Reads all remaining rows.
|
||||
* \details
|
||||
* The contents of `output` will be replaced by the read rows, so you can obtain the number of
|
||||
* read rows using `output.size()`. After this operation succeeds, `this->complete() == true`.
|
||||
* \n
|
||||
* This function requires fitting all the rows in the resultset into memory. It is a good choice
|
||||
* for small resultsets.
|
||||
* \brief Returns the number of warnings produced by the executed SQL statement.
|
||||
* \details Precondition: `this->has_value() == true`.
|
||||
*/
|
||||
void read_all(rows& output, error_code& err, error_info& info);
|
||||
|
||||
/// \copydoc read_all(rows&,error_code&,error_info&)
|
||||
void read_all(rows& output);
|
||||
|
||||
/**
|
||||
* \copydoc read_all(rows&,error_code&,error_info&)
|
||||
* The handler signature for this operation is `void(boost::mysql::error_code)`.
|
||||
*/
|
||||
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code))
|
||||
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_read_all(
|
||||
rows& output,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
)
|
||||
unsigned warning_count() const noexcept
|
||||
{
|
||||
return async_read_all(
|
||||
output,
|
||||
get_channel().shared_info(),
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
assert(has_value());
|
||||
return st_.warning_count();
|
||||
}
|
||||
|
||||
/// \copydoc async_read_all(rows&,CompletionToken&&)
|
||||
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code))
|
||||
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_read_all(
|
||||
rows& output,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
);
|
||||
|
||||
/**
|
||||
* \brief Reads all remaining rows.
|
||||
* \details
|
||||
* The contents of `output` will be replaced by the read rows, so you can obtain the number of
|
||||
* read rows using `output.size()`. After this operation succeeds, `this->complete() == true`.
|
||||
* \n
|
||||
* This function requires fitting all the rows in the resultset into memory. It is a good choice
|
||||
* for small resultsets.
|
||||
* \n
|
||||
* The returned view points into the \ref connection that `*this` references.
|
||||
* It will be valid until the `connection` performs any other read operation
|
||||
* or is destroyed.
|
||||
* \brief Returns additionat text information about the execution of the SQL statement.
|
||||
* \details Precondition: `this->has_value() == true`.
|
||||
*\n
|
||||
* The format of this information is documented by MySQL <a
|
||||
* href="https://dev.mysql.com/doc/c-api/8.0/en/mysql-info.html">here</a>.
|
||||
*\n
|
||||
* The returned string always uses ASCII encoding, regardless of the connection's character set.
|
||||
*\n
|
||||
* This function returns a view object, with reference semantics. The returned view points into
|
||||
* memory owned by `*this`, and will be valid as long as `*this` or an object move-constructed
|
||||
* from `*this` are alive.
|
||||
*/
|
||||
rows_view read_all(use_views_t, error_code& err, error_info& info);
|
||||
|
||||
/// \copydoc read_all(use_views_t,error_code&,error_info&)
|
||||
rows_view read_all(use_views_t);
|
||||
|
||||
/**
|
||||
* \copydoc read_all(use_views_t,error_code&,error_info&)
|
||||
* \details
|
||||
* The handler signature for this operation is `void(boost::mysql::error_code)`.
|
||||
*/
|
||||
template <
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, ::boost::mysql::rows_view))
|
||||
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, rows_view))
|
||||
async_read_all(
|
||||
use_views_t,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
)
|
||||
boost::string_view info() const noexcept
|
||||
{
|
||||
return async_read_all(
|
||||
use_views,
|
||||
get_channel().shared_info(),
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
assert(has_value());
|
||||
return st_.info();
|
||||
}
|
||||
|
||||
/// \copydoc async_read_all(use_views_t,CompletionToken&&)
|
||||
template <
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, ::boost::mysql::rows_view))
|
||||
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, rows_view))
|
||||
async_read_all(
|
||||
use_views_t,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
);
|
||||
|
||||
private:
|
||||
detail::channel<Stream>& get_channel() noexcept
|
||||
{
|
||||
assert(valid());
|
||||
return *static_cast<detail::channel<Stream>*>(channel_ptr());
|
||||
}
|
||||
execution_state st_;
|
||||
::boost::mysql::rows rows_;
|
||||
};
|
||||
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#include <boost/mysql/impl/resultset.hpp>
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,195 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_MYSQL_RESULTSET_BASE_HPP
|
||||
#define BOOST_MYSQL_RESULTSET_BASE_HPP
|
||||
|
||||
#include <boost/mysql/detail/protocol/common_messages.hpp>
|
||||
#include <boost/mysql/detail/protocol/deserialization_context.hpp>
|
||||
#include <boost/mysql/detail/protocol/resultset_encoding.hpp>
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/metadata.hpp>
|
||||
#include <boost/mysql/metadata_collection_view.hpp>
|
||||
#include <boost/mysql/row.hpp>
|
||||
|
||||
#include <boost/utility/string_view.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
|
||||
/**
|
||||
* \brief The base class for resultsets.
|
||||
* \details Don't instantiate this class directly - use \ref resultset instead.
|
||||
*\n
|
||||
* All member functions, except otherwise noted, have `this->valid()` as precondition.
|
||||
* Calling any function on an invalid resultset results in undefined behavior.
|
||||
*/
|
||||
class resultset_base
|
||||
{
|
||||
class ok_packet_data
|
||||
{
|
||||
bool has_data_{false};
|
||||
std::uint64_t affected_rows_;
|
||||
std::uint64_t last_insert_id_;
|
||||
std::uint16_t warnings_;
|
||||
std::string info_;
|
||||
|
||||
public:
|
||||
ok_packet_data() = default;
|
||||
ok_packet_data(const detail::ok_packet& pack) { assign(pack); }
|
||||
|
||||
void reset() noexcept { has_data_ = false; }
|
||||
void assign(const detail::ok_packet& pack)
|
||||
{
|
||||
has_data_ = true;
|
||||
affected_rows_ = pack.affected_rows.value;
|
||||
last_insert_id_ = pack.last_insert_id.value;
|
||||
warnings_ = pack.warnings;
|
||||
info_.assign(pack.info.value.begin(), pack.info.value.end());
|
||||
}
|
||||
|
||||
bool has_value() const noexcept { return has_data_; }
|
||||
std::uint64_t affected_rows() const noexcept
|
||||
{
|
||||
assert(has_data_);
|
||||
return affected_rows_;
|
||||
}
|
||||
std::uint64_t last_insert_id() const noexcept
|
||||
{
|
||||
assert(has_data_);
|
||||
return last_insert_id_;
|
||||
}
|
||||
unsigned warning_count() const noexcept
|
||||
{
|
||||
assert(has_data_);
|
||||
return warnings_;
|
||||
}
|
||||
boost::string_view info() const noexcept
|
||||
{
|
||||
assert(has_data_);
|
||||
return info_;
|
||||
}
|
||||
};
|
||||
|
||||
void* channel_{nullptr};
|
||||
std::uint8_t seqnum_{};
|
||||
detail::resultset_encoding encoding_{detail::resultset_encoding::text};
|
||||
std::vector<metadata> meta_;
|
||||
ok_packet_data ok_packet_;
|
||||
|
||||
public:
|
||||
#ifndef BOOST_MYSQL_DOXYGEN
|
||||
// Private, do not use. TODO: hide these
|
||||
resultset_base() = default;
|
||||
void reset(void* channel, detail::resultset_encoding encoding) noexcept
|
||||
{
|
||||
channel_ = channel;
|
||||
seqnum_ = 0;
|
||||
encoding_ = encoding;
|
||||
meta_.clear();
|
||||
ok_packet_.reset();
|
||||
}
|
||||
|
||||
void complete(const detail::ok_packet& ok_pack)
|
||||
{
|
||||
assert(valid());
|
||||
ok_packet_.assign(ok_pack);
|
||||
}
|
||||
|
||||
void prepare_meta(std::size_t num_fields) { meta_.reserve(num_fields); }
|
||||
|
||||
void add_meta(const detail::column_definition_packet& pack) { meta_.emplace_back(pack, true); }
|
||||
|
||||
detail::resultset_encoding encoding() const noexcept { return encoding_; }
|
||||
|
||||
std::uint8_t& sequence_number() noexcept { return seqnum_; }
|
||||
|
||||
std::vector<metadata>& fields() noexcept { return meta_; }
|
||||
const std::vector<metadata>& fields() const noexcept { return meta_; }
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Returns `true` if the object represents an actual resultset.
|
||||
* \details Calling any function other than assignment on a resultset for which
|
||||
* this function returns `false` results in undefined behavior.
|
||||
*
|
||||
* To be usable for server communication, the \ref connection referenced by this object must be
|
||||
* alive and open, too.
|
||||
*
|
||||
* Returns `false` for default-constructed and moved-from objects.
|
||||
*/
|
||||
bool valid() const noexcept { return channel_ != nullptr; }
|
||||
|
||||
/**
|
||||
* \brief Returns whether the resultset has been completely read or not.
|
||||
* \details
|
||||
* After a resultset is `complete`, you may access extra information about the operation, like
|
||||
* \ref affected_rows or \ref last_insert_id.
|
||||
*/
|
||||
bool complete() const noexcept { return ok_packet_.has_value(); }
|
||||
|
||||
/**
|
||||
* \brief Returns metadata about the columns in the query.
|
||||
* \details
|
||||
* The returned collection will have as many \ref metadata objects as columns retrieved by
|
||||
* the SQL query, and in the same order.
|
||||
*\n
|
||||
* This function returns a view object, with reference semantics. This view object references
|
||||
* `*this` internal state, and will be valid as long as `*this` (or a `resultset`
|
||||
* move-constructed from `*this`) is alive.
|
||||
*/
|
||||
metadata_collection_view meta() const noexcept
|
||||
{
|
||||
return metadata_collection_view(meta_.data(), meta_.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief The number of rows affected by the SQL statement that generated this resultset.
|
||||
* \details The resultset **must be complete**
|
||||
* before calling this function.
|
||||
*/
|
||||
std::uint64_t affected_rows() const noexcept { return ok_packet_.affected_rows(); }
|
||||
|
||||
/**
|
||||
* \brief The last insert ID produced by the SQL statement that generated this resultset.
|
||||
* \details The resultset **must be complete** before calling this function.
|
||||
*/
|
||||
std::uint64_t last_insert_id() const noexcept { return ok_packet_.last_insert_id(); }
|
||||
|
||||
/**
|
||||
* \brief The number of warnings produced by the SQL statement that generated this resultset.
|
||||
* \details The resultset **must be complete** before calling this function.
|
||||
*/
|
||||
unsigned warning_count() const noexcept { return ok_packet_.warning_count(); }
|
||||
|
||||
/**
|
||||
* \brief Additionat text information about the execution of
|
||||
* the SQL statement that generated this resultset.
|
||||
* \details The resultset **must be complete** before calling this function.
|
||||
*\n
|
||||
* This function returns a view object, with reference semantics. This view object references
|
||||
* `*this` internal state, and will be valid as long as `*this` (or a `resultset`
|
||||
* move-constructed from `*this`) is alive.
|
||||
*/
|
||||
boost::string_view info() const noexcept { return ok_packet_.info(); }
|
||||
|
||||
protected:
|
||||
void* channel_ptr() noexcept { return channel_; }
|
||||
void reset() noexcept { reset(nullptr, detail::resultset_encoding::text); }
|
||||
void swap(resultset_base& other) noexcept { std::swap(*this, other); }
|
||||
};
|
||||
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
|
@ -8,11 +8,12 @@
|
|||
#ifndef BOOST_MYSQL_ROW_HPP
|
||||
#define BOOST_MYSQL_ROW_HPP
|
||||
|
||||
#include <boost/mysql/detail/auxiliar/row_base.hpp>
|
||||
#include <boost/mysql/field.hpp>
|
||||
#include <boost/mysql/field_view.hpp>
|
||||
#include <boost/mysql/row_view.hpp>
|
||||
|
||||
#include <boost/mysql/detail/auxiliar/row_base.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <iosfwd>
|
||||
#include <vector>
|
||||
|
@ -28,12 +29,9 @@ namespace mysql {
|
|||
* `row`'s internal storage. These views behave like references, and are valid as long as pointers,
|
||||
* iterators and references into the `row` remain valid.
|
||||
* \n
|
||||
* Objects of this type can be populated by the \ref resultset::read_one function family. You may
|
||||
* create a `row` directly to persist a \ref row_view, too.
|
||||
* \n
|
||||
* Although owning, `row` is read-only.
|
||||
* It's optimized for memory re-use in \ref resultset::read_one loops. If you need to mutate fields,
|
||||
* use a `std::vector<field>` instead (see \ref row_view::as_vector and \ref row::as_vector).
|
||||
* Although owning, `row` is read-only. It's optimized for memory re-use. If you need to mutate
|
||||
* fields, use a `std::vector<field>` instead (see \ref row_view::as_vector and \ref
|
||||
* row::as_vector).
|
||||
*/
|
||||
class row : private detail::row_base
|
||||
{
|
||||
|
|
|
@ -25,15 +25,14 @@ namespace mysql {
|
|||
* A `row_view` points to memory owned by an external entity (like `string_view` does). The validity
|
||||
* of a `row_view` depends on how it was obtained:
|
||||
* \n
|
||||
* - If it was constructed from a \ref row object (by calling \ref row::operator row_view()), the
|
||||
* view acts as a reference to the row's allocated memory, and is valid as long as references
|
||||
* to that row element's are valid.
|
||||
* - If it was obtained by indexing a \ref rows object, the same applies.
|
||||
* - If it was obtained by indexing a \ref rows_view object, it's valid as long as the
|
||||
* \li If it was constructed from a \ref row object (by calling \ref row::operator row_view()), the
|
||||
* view acts as a reference to the row's allocated memory, and is valid as long as references
|
||||
* to that row element's are valid.
|
||||
* \li If it was obtained by indexing a \ref rows object, the same applies.
|
||||
* \li If it was obtained by indexing a \ref rows_view object, it's valid as long as the
|
||||
* `rows_view` is valid.
|
||||
* - If it was obtained by a call to `resultset::read_xxx` or similar functions taking a \ref
|
||||
* use_views_t parameter, it's valid until the underlying \ref connection performs the next
|
||||
* network call or is destroyed.
|
||||
* \li If it was obtained by a call to \ref connection::read_one_row, it's valid until the
|
||||
* `connection` performs the next network call or is destroyed.
|
||||
* \n
|
||||
* Calling any member function on an invalid view results in undefined behavior.
|
||||
* \n
|
||||
|
|
|
@ -8,13 +8,14 @@
|
|||
#ifndef BOOST_MYSQL_ROWS_HPP
|
||||
#define BOOST_MYSQL_ROWS_HPP
|
||||
|
||||
#include <boost/mysql/detail/auxiliar/row_base.hpp>
|
||||
#include <boost/mysql/detail/auxiliar/rows_iterator.hpp>
|
||||
#include <boost/mysql/field_view.hpp>
|
||||
#include <boost/mysql/row.hpp>
|
||||
#include <boost/mysql/row_view.hpp>
|
||||
#include <boost/mysql/rows_view.hpp>
|
||||
|
||||
#include <boost/mysql/detail/auxiliar/row_base.hpp>
|
||||
#include <boost/mysql/detail/auxiliar/rows_iterator.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
|
||||
|
@ -24,19 +25,14 @@ namespace mysql {
|
|||
* Models an owning, matrix-like container. Indexing a `rows` object (by using iterators,
|
||||
* \ref rows::at or \ref rows::operator[]) returns a \ref row_view object, representing a
|
||||
* single row. All rows in the collection are the same size (as given by \ref num_columns).
|
||||
*
|
||||
*\n
|
||||
* A `rows` object owns a chunk of memory in which it stores its elements. The \ref rows_view
|
||||
* objects obtained on element access point into the `rows`' internal storage. These views (and any
|
||||
* \ref row_view and \ref field_view obtained from the former) behave
|
||||
* like references, and are valid as long as pointers, iterators and references into the `rows`
|
||||
* object remain valid.
|
||||
\n
|
||||
* Objects of this type can be populated by the \ref resultset::read_some and \ref
|
||||
* resultset::read_all function family. You may create a `rows` object directly to persist \ref
|
||||
* rows_view objects, too.
|
||||
\n
|
||||
* Although owning, `rows` is read-only. It's optimized for memory re-use in \ref
|
||||
* resultset::read_some and \ref resultset::read_all loops.
|
||||
* Although owning, `rows` is read-only. It's optimized for memory re-use.
|
||||
*/
|
||||
class rows : private detail::row_base
|
||||
{
|
||||
|
|
|
@ -8,11 +8,12 @@
|
|||
#ifndef BOOST_MYSQL_ROWS_VIEW_HPP
|
||||
#define BOOST_MYSQL_ROWS_VIEW_HPP
|
||||
|
||||
#include <boost/mysql/detail/auxiliar/rows_iterator.hpp>
|
||||
#include <boost/mysql/field_view.hpp>
|
||||
#include <boost/mysql/row.hpp>
|
||||
#include <boost/mysql/row_view.hpp>
|
||||
|
||||
#include <boost/mysql/detail/auxiliar/rows_iterator.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace boost {
|
||||
|
@ -26,13 +27,12 @@ namespace mysql {
|
|||
* single row. All rows in the collection are the same size (as given by \ref num_columns).
|
||||
* \n
|
||||
* A `rows_view` object points to memory owned by an external entity (like `string_view` does). The
|
||||
* validity of a `rows_view` object depends on how it was obtained: \n
|
||||
* - If it was constructed from a \ref rows object (by calling \ref rows::operator rows_view()),
|
||||
* the view acts as a reference to the `rows`' allocated memory, and is valid as long as references
|
||||
* to that `rows` element's are valid.
|
||||
* - If it was obtained by a call to `resultset::read_xxx` or similar functions taking a \ref
|
||||
* use_views_t parameter, it's valid until the underlying \ref connection performs the next
|
||||
* network call or is destroyed.
|
||||
* validity of a `rows_view` object depends on how it was obtained:
|
||||
* \li If it was constructed from a \ref rows object (by calling \ref rows::operator rows_view()),
|
||||
* the view acts as a reference to the `rows`' allocated memory, and is valid as long as
|
||||
* references to that `rows` element's are valid.
|
||||
* \li If it was obtained by calling \ref connection::read_some_rows it's valid until the
|
||||
* `connection` performs the next network call or is destroyed.
|
||||
* \n
|
||||
* \ref row_view's and \ref field_view's obtained by using a `rows_view` object are valid as long as
|
||||
* the underlying storage that `*this` points to is valid. Destroying `*this` doesn't invalidate
|
||||
|
@ -150,10 +150,12 @@ public:
|
|||
|
||||
#ifndef BOOST_MYSQL_DOXYGEN
|
||||
// TODO: hide this
|
||||
rows_view(const field_view* fields, std::size_t num_values, std::size_t num_columns) noexcept
|
||||
: fields_(fields), num_fields_(num_values), num_columns_(num_columns)
|
||||
rows_view(const field_view* fields, std::size_t num_fields, std::size_t num_columns) noexcept
|
||||
: fields_(fields), num_fields_(num_fields), num_columns_(num_columns)
|
||||
{
|
||||
assert(num_values % num_columns == 0);
|
||||
assert(fields != nullptr || num_fields == 0); // fields null => num_fields 0
|
||||
assert(num_fields == 0 || num_columns != 0); // num_fields != 0 => num_columns != 0
|
||||
assert(num_columns == 0 || (num_fields % num_columns == 0));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -8,12 +8,13 @@
|
|||
#ifndef BOOST_MYSQL_STATEMENT_HPP
|
||||
#define BOOST_MYSQL_STATEMENT_HPP
|
||||
|
||||
#include <boost/mysql/detail/auxiliar/field_type_traits.hpp>
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
#include <boost/mysql/execute_options.hpp>
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
#include <boost/mysql/resultset.hpp>
|
||||
#include <boost/mysql/statement_base.hpp>
|
||||
|
||||
#include <boost/mysql/detail/auxiliar/field_type_traits.hpp>
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <iterator>
|
||||
|
||||
|
@ -23,15 +24,11 @@ namespace mysql {
|
|||
/**
|
||||
* \brief Represents a server-side prepared statement.
|
||||
* \details
|
||||
* You can obtain a `valid` statement by calling \ref connection::prepare_statement. You can execute
|
||||
* a statement using \ref statement::execute, and close it using \ref statement::close.
|
||||
*\n
|
||||
* Statements are proxy I/O objects, meaning that they hold a reference to the internal state of the
|
||||
* \ref connection that created them. I/O operations on a statement result in reads and writes on
|
||||
* the connection's stream. A `statement` is usable for I/O operations as long as the original \ref
|
||||
* connection is alive and open. Moving the `connection` object doesn't invalidate `statement`s
|
||||
* pointing into it, but destroying or closing it does. The executor object used by a `statement` is
|
||||
* always the underlying stream's.
|
||||
* the connection's stream. A `statement` is usable for I/O operations as long as the \ref
|
||||
* connection that created them (or a connection move-constructed from it) is alive and open.
|
||||
* The executor object used by a `statement` is always the underlying stream's.
|
||||
*\n
|
||||
* Statements are default-constructible and movable, but not copyable. A default constructed or
|
||||
* closed statement has `!this->valid()`. Calling any member function on an invalid
|
||||
|
@ -43,8 +40,7 @@ class statement : public statement_base
|
|||
public:
|
||||
/**
|
||||
* \brief Default constructor.
|
||||
* \details Default constructed statements have `!this->valid()`. To obtain a valid statement,
|
||||
* call \ref connection::prepare_statement.
|
||||
* \details Default constructed statements have `this->valid() == false`.
|
||||
*/
|
||||
statement() = default;
|
||||
|
||||
|
@ -80,22 +76,16 @@ public:
|
|||
executor_type get_executor() { return get_channel().get_executor(); }
|
||||
|
||||
/**
|
||||
* \brief Executes a prepared statement, passing parameters as a tuple.
|
||||
* \brief Executes a prepared statement.
|
||||
* \details
|
||||
* Starts a multi-function operation. This function will write the execute request to the
|
||||
* server and read the initial server response, but won't read the generated rows, if any. After
|
||||
* this operation completes, `result` will have \ref resultset::meta populated, and may become
|
||||
* \ref resultset::complete, if the operation did not generate any rows (e.g. it was an
|
||||
* `UPDATE`). `result` will reference the same \ref connection object that `*this` references,
|
||||
* and will be usable for server interaction as long as I/O object references to `*this` are
|
||||
* valid.
|
||||
* Executes the statement with the given parameters and reads the response into `result`.
|
||||
*\n
|
||||
* If the operation generated any rows, these __must__ be read (by using any of the
|
||||
* `resultset::read_xxx` functions) before engaging in any further operation involving server
|
||||
* communication. Otherwise, the results are undefined.
|
||||
* After this operation completes successfully, `result.has_value() == true`.
|
||||
*\n
|
||||
* The statement actual parameters (`params`) are passed as a `std::tuple` of elements.
|
||||
* See the `FieldLikeTuple` concept defition for more info.
|
||||
* See the `FieldLikeTuple` concept defition for more info. You should pass exactly as many
|
||||
* parameters as `this->num_params()`, or the operation will fail with an error.
|
||||
* String parameters should be encoded using the connection's character set.
|
||||
*\n
|
||||
* This operation involves both reads and writes on the underlying stream.
|
||||
*/
|
||||
|
@ -104,22 +94,16 @@ public:
|
|||
class EnableIf = detail::enable_if_field_like_tuple<FieldLikeTuple>>
|
||||
void execute(
|
||||
const FieldLikeTuple& params,
|
||||
resultset<Stream>& result,
|
||||
resultset& result,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
execute(params, execute_options(), result, err, info);
|
||||
}
|
||||
);
|
||||
|
||||
/// \copydoc execute
|
||||
template <
|
||||
BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple,
|
||||
class EnableIf = detail::enable_if_field_like_tuple<FieldLikeTuple>>
|
||||
void execute(const FieldLikeTuple& params, resultset<Stream>& result)
|
||||
{
|
||||
execute(params, execute_options(), result);
|
||||
}
|
||||
void execute(const FieldLikeTuple& params, resultset& result);
|
||||
|
||||
/**
|
||||
* \copydoc execute
|
||||
|
@ -138,80 +122,12 @@ public:
|
|||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_execute(
|
||||
FieldLikeTuple&& params,
|
||||
resultset<Stream>& result,
|
||||
resultset& result,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
)
|
||||
{
|
||||
return async_execute(
|
||||
std::forward<FieldLikeTuple>(params),
|
||||
execute_options(),
|
||||
result,
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
}
|
||||
|
||||
/// \copydoc async_execute
|
||||
template <
|
||||
BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code))
|
||||
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type),
|
||||
class EnableIf = detail::enable_if_field_like_tuple<FieldLikeTuple>>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_execute(
|
||||
FieldLikeTuple&& params,
|
||||
resultset<Stream>& result,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
)
|
||||
{
|
||||
return async_execute(
|
||||
std::forward<FieldLikeTuple>(params),
|
||||
execute_options(),
|
||||
result,
|
||||
output_info,
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
}
|
||||
|
||||
/// \copydoc execute
|
||||
template <
|
||||
BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple,
|
||||
class EnableIf = detail::enable_if_field_like_tuple<FieldLikeTuple>>
|
||||
void execute(
|
||||
const FieldLikeTuple& params,
|
||||
const execute_options& opts,
|
||||
resultset<Stream>& result,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
);
|
||||
|
||||
/// \copydoc execute
|
||||
template <
|
||||
BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple,
|
||||
class EnableIf = detail::enable_if_field_like_tuple<FieldLikeTuple>>
|
||||
void execute(
|
||||
const FieldLikeTuple& params,
|
||||
const execute_options& opts,
|
||||
resultset<Stream>& result
|
||||
);
|
||||
|
||||
/// \copydoc async_execute
|
||||
template <
|
||||
BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code))
|
||||
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type),
|
||||
class EnableIf = detail::enable_if_field_like_tuple<FieldLikeTuple>>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_execute(
|
||||
FieldLikeTuple&& params,
|
||||
const execute_options& opts,
|
||||
resultset<Stream>& result,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
)
|
||||
{
|
||||
return async_execute(
|
||||
std::forward<FieldLikeTuple>(params),
|
||||
opts,
|
||||
result,
|
||||
get_channel().shared_info(),
|
||||
std::forward<CompletionToken>(token)
|
||||
|
@ -227,60 +143,134 @@ public:
|
|||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_execute(
|
||||
FieldLikeTuple&& params,
|
||||
const execute_options& opts,
|
||||
resultset<Stream>& result,
|
||||
resultset& result,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
);
|
||||
|
||||
/**
|
||||
* \brief (Experimental) Executes a prepared statement, passing parameters as a range.
|
||||
* \brief Starts a statement execution as a multi-function operation.
|
||||
* \details
|
||||
* [warning This function is experimental. Details may change in the future without notice.]
|
||||
* Writes the execute request and reads the initial server response and the column
|
||||
* metadata, but not the generated rows, if any. After this operation completes, `st` will have
|
||||
* \ref execution_state::meta populated, and may become \ref execution_state::complete
|
||||
* if the operation did not generate any rows (e.g. it was an `UPDATE`).
|
||||
*\n
|
||||
* If the operation generated any rows, these <b>must</b> be read (by using \ref
|
||||
*connection::read_one_row or \ref connection::read_some_rows) before engaging in any further
|
||||
*operation involving server communication. Otherwise, the results are undefined.
|
||||
*\n
|
||||
* The statement actual parameters (`params`) are passed as a `std::tuple` of elements.
|
||||
* See the `FieldLikeTuple` concept defition for more info. You should pass exactly as many
|
||||
* parameters as `this->num_params()`, or the operation will fail with an error.
|
||||
* String parameters should be encoded using the connection's character set.
|
||||
*\n
|
||||
* This operation involves both reads and writes on the underlying stream.
|
||||
*/
|
||||
template <
|
||||
BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple,
|
||||
class EnableIf = detail::enable_if_field_like_tuple<FieldLikeTuple>>
|
||||
void start_execution(
|
||||
const FieldLikeTuple& params,
|
||||
execution_state& ex,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
);
|
||||
|
||||
/// \copydoc start_execution(const FieldLikeTuple&,execution_state&,error_code&,error_info&)
|
||||
template <
|
||||
BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple,
|
||||
class EnableIf = detail::enable_if_field_like_tuple<FieldLikeTuple>>
|
||||
void start_execution(const FieldLikeTuple& params, execution_state& st);
|
||||
|
||||
/**
|
||||
* \copydoc start_execution(const FieldLikeTuple&,execution_state&,error_code&,error_info&)
|
||||
* \details
|
||||
* If `CompletionToken` is deferred (like `use_awaitable`), and `params` contains any reference
|
||||
* type (like `string_view`), the caller must keep the values pointed by these references alive
|
||||
* until the operation is initiated. Value types will be copied/moved as required, so don't need
|
||||
* to be kept alive.
|
||||
*
|
||||
* Starts a multi-function operation. This function will write the execute request to the
|
||||
* server and read the initial server response, but won't read the generated rows, if any. After
|
||||
* this operation completes, `result` will have \ref resultset::meta populated, and may become
|
||||
* \ref resultset::complete, if the operation did not generate any rows (e.g. it was an
|
||||
* `UPDATE`). `result` will reference the same \ref connection object that `*this` references,
|
||||
* and will be usable for server interaction as long as I/O object references to `*this` are
|
||||
* valid.
|
||||
* The handler signature for this operation is `void(boost::mysql::error_code)`.
|
||||
*/
|
||||
template <
|
||||
BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code))
|
||||
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type),
|
||||
class EnableIf = detail::enable_if_field_like_tuple<FieldLikeTuple>>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_start_execution(
|
||||
FieldLikeTuple&& params,
|
||||
execution_state& st,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
)
|
||||
{
|
||||
return async_start_execution(
|
||||
std::forward<FieldLikeTuple>(params),
|
||||
st,
|
||||
get_channel().shared_info(),
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
}
|
||||
|
||||
/// \copydoc async_start_execution(FieldLikeTuple&&,execution_state&,CompletionToken&&)
|
||||
template <
|
||||
BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code))
|
||||
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type),
|
||||
class EnableIf = detail::enable_if_field_like_tuple<FieldLikeTuple>>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_start_execution(
|
||||
FieldLikeTuple&& params,
|
||||
execution_state& st,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
);
|
||||
|
||||
/**
|
||||
* \brief Starts a statement execution as a multi-function operation.
|
||||
* \details
|
||||
* <b>Warning: this function is experimental. Details may change in the future without
|
||||
* notice.</b>
|
||||
*\n
|
||||
* If the operation generated any rows, these __must__ be read (by using any of the
|
||||
* `resultset::read_xxx` functions) before engaging in any further operation involving server
|
||||
* communication. Otherwise, the results are undefined.
|
||||
* Writes the execute request and reads the initial server response and the column
|
||||
* metadata, but not the generated rows, if any. After this operation completes, `st` will have
|
||||
* \ref execution_state::meta populated, and may become \ref execution_state::complete
|
||||
* if the operation did not generate any rows (e.g. it was an `UPDATE`).
|
||||
*\n
|
||||
* The statement actual parameters are passed as an iterator range. There should be
|
||||
* __exactly__ as many parameters as required (as given by \ref statement::num_params).
|
||||
* Dereferencing the passed iterators should yield a type convertible to \ref field_view.
|
||||
* Both \ref field and \ref field_view satisfy this.
|
||||
* If the operation generated any rows, these <b>must</b> be read (by using \ref
|
||||
* connection::read_one_row or \ref connection::read_some_rows) before engaging in any further
|
||||
* operation involving server communication. Otherwise, the results are undefined.
|
||||
*\n
|
||||
* The statement actual parameters are passed as an iterator range.
|
||||
* See the `FieldViewForwardIterator` concept defition for more info. You should pass exactly as
|
||||
* many parameters as `this->num_params()`, or the operation will fail with an error. String
|
||||
* parameters should be encoded using the connection's character set.
|
||||
*\n
|
||||
* This operation involves both reads and writes on the underlying stream.
|
||||
*/
|
||||
template <BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator>
|
||||
void execute(
|
||||
void start_execution(
|
||||
FieldViewFwdIterator params_first,
|
||||
FieldViewFwdIterator params_last,
|
||||
const execute_options& options,
|
||||
resultset<Stream>& result,
|
||||
execution_state& st,
|
||||
error_code& ec,
|
||||
error_info& info
|
||||
);
|
||||
|
||||
/// \copydoc execute(FieldViewFwdIterator,FieldViewFwdIterator,const execute_options&,resultset<Stream>&,error_code&,error_info&)
|
||||
/// \copydoc start_execution(FieldViewFwdIterator,FieldViewFwdIterator,execution_state&,error_code&,error_info&)
|
||||
template <BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator>
|
||||
void execute(
|
||||
void start_execution(
|
||||
FieldViewFwdIterator params_first,
|
||||
FieldViewFwdIterator params_last,
|
||||
const execute_options& options,
|
||||
resultset<Stream>& result
|
||||
execution_state& st
|
||||
);
|
||||
|
||||
/**
|
||||
* \copydoc execute(FieldViewFwdIterator,FieldViewFwdIterator,const execute_options&,resultset<Stream>&,error_code&,error_info&)
|
||||
* \copydoc start_execution(FieldViewFwdIterator,FieldViewFwdIterator,execution_state&,error_code&,error_info&)
|
||||
* \details
|
||||
* If `CompletionToken` is deferred (like `use_awaitable`), the caller must keep objects in
|
||||
* the range `\\[params_first, params_last)` alive until the operation is initiated.
|
||||
* the iterator range alive until the operation is initiated.
|
||||
*
|
||||
* The handler signature for this operation is `void(boost::mysql::error_code)`.
|
||||
*/
|
||||
|
@ -289,41 +279,38 @@ public:
|
|||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code))
|
||||
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_execute(
|
||||
async_start_execution(
|
||||
FieldViewFwdIterator params_first,
|
||||
FieldViewFwdIterator params_last,
|
||||
const execute_options& options,
|
||||
resultset<Stream>& result,
|
||||
execution_state& st,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
)
|
||||
{
|
||||
return async_execute(
|
||||
return async_start_execution(
|
||||
params_first,
|
||||
params_last,
|
||||
options,
|
||||
result,
|
||||
st,
|
||||
get_channel().shared_info(),
|
||||
std::forward<CompletionToken>(token)
|
||||
);
|
||||
}
|
||||
|
||||
/// \copydoc async_execute(FieldViewFwdIterator,FieldViewFwdIterator,const execute_options&,resultset<Stream>&,CompletionToken&&)
|
||||
/// \copydoc async_start_execution(FieldViewFwdIterator,FieldViewFwdIterator,execution_state&,CompletionToken&&)
|
||||
template <
|
||||
BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code))
|
||||
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
|
||||
async_execute(
|
||||
async_start_execution(
|
||||
FieldViewFwdIterator params_first,
|
||||
FieldViewFwdIterator params_last,
|
||||
const execute_options& options,
|
||||
resultset<Stream>& result,
|
||||
execution_state& st,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
|
||||
);
|
||||
|
||||
/**
|
||||
* \brief Closes a prepared statement, deallocating it from the server.
|
||||
* \brief Closes a statement, deallocating it from the server.
|
||||
* \details
|
||||
* After this operation succeeds, `this->valid()` will return `false`, and no further functions
|
||||
* may be called on this prepared statement, other than assignment.
|
||||
|
|
|
@ -8,9 +8,10 @@
|
|||
#ifndef BOOST_MYSQL_STATEMENT_BASE_HPP
|
||||
#define BOOST_MYSQL_STATEMENT_BASE_HPP
|
||||
|
||||
#include <boost/mysql/detail/protocol/prepared_statement_messages.hpp>
|
||||
#include <boost/mysql/field_view.hpp>
|
||||
|
||||
#include <boost/mysql/detail/protocol/prepared_statement_messages.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <tuple>
|
||||
|
@ -18,9 +19,6 @@
|
|||
namespace boost {
|
||||
namespace mysql {
|
||||
|
||||
/// Convenience constant to use when executing a statement without parameters.
|
||||
constexpr std::tuple<> no_statement_params{};
|
||||
|
||||
/**
|
||||
* \brief The base class for prepared statements.
|
||||
* \details Don't instantiate this class directly - use \ref statement instead.
|
||||
|
@ -47,11 +45,11 @@ public:
|
|||
* \brief Returns `true` if the object represents an actual server statement.
|
||||
* \details Calling any function other than assignment on a statement for which
|
||||
* this function returns `false` results in undefined behavior.
|
||||
*
|
||||
*\n
|
||||
* To be usable for server communication, the \ref connection referenced by this object must be
|
||||
* alive and open, too.
|
||||
*
|
||||
* Returns `false` for default-constructed, moved-from and closed statements.
|
||||
*\n
|
||||
* Returns `false` for default-constructed and closed statements.
|
||||
*/
|
||||
bool valid() const noexcept { return channel_ != nullptr; }
|
||||
|
||||
|
|
|
@ -21,9 +21,6 @@ using tcp_connection = connection<boost::asio::ip::tcp::socket>;
|
|||
/// The statement type to use with \ref tcp_connection.
|
||||
using tcp_statement = typename tcp_connection::statement_type;
|
||||
|
||||
/// The resultset type to use with \ref tcp_connection.
|
||||
using tcp_resultset = typename tcp_connection::resultset_type;
|
||||
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
|
|
|
@ -22,9 +22,6 @@ using tcp_ssl_connection = connection<boost::asio::ssl::stream<boost::asio::ip::
|
|||
/// The statement type to use with \ref tcp_ssl_connection.
|
||||
using tcp_ssl_statement = typename tcp_ssl_connection::statement_type;
|
||||
|
||||
/// The resultset type to use with \ref tcp_ssl_connection.
|
||||
using tcp_ssl_resultset = typename tcp_ssl_connection::resultset_type;
|
||||
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
|
|
|
@ -23,9 +23,6 @@ using unix_connection = connection<boost::asio::local::stream_protocol::socket>;
|
|||
/// The statement type to use with \ref unix_connection.
|
||||
using unix_statement = typename unix_connection::statement_type;
|
||||
|
||||
/// The resultset type to use with \ref unix_connection.
|
||||
using unix_resultset = typename unix_connection::resultset_type;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace mysql
|
||||
|
|
|
@ -25,9 +25,6 @@ using unix_ssl_connection = connection<
|
|||
/// The statement type to use with \ref unix_ssl_connection.
|
||||
using unix_ssl_statement = typename unix_ssl_connection::statement_type;
|
||||
|
||||
/// The resultset type to use with \ref unix_ssl_connection.
|
||||
using unix_ssl_resultset = typename unix_ssl_connection::resultset_type;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace mysql
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_MYSQL_USE_VIEWS_HPP
|
||||
#define BOOST_MYSQL_USE_VIEWS_HPP
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
|
||||
/**
|
||||
* \brief Placeholder type to indicate that a function should return non-owning views.
|
||||
*/
|
||||
struct use_views_t
|
||||
{
|
||||
};
|
||||
|
||||
/// Placeholder to indicate that a function should return non-owning views.
|
||||
constexpr use_views_t use_views{};
|
||||
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
|
@ -4,7 +4,7 @@
|
|||
"authors": [
|
||||
"Rubén Pérez"
|
||||
],
|
||||
"description": "Async MySQL client",
|
||||
"description": "MySQL client library built on top of Boost.Asio.",
|
||||
"category": [
|
||||
"Concurrent",
|
||||
"IO"
|
||||
|
@ -13,4 +13,4 @@
|
|||
"Rubén Pérez <rubenperez038@gmail.com>"
|
||||
],
|
||||
"cxxstd": "11"
|
||||
}
|
||||
}
|
243
test/Jamfile
243
test/Jamfile
|
@ -6,11 +6,114 @@
|
|||
#
|
||||
|
||||
import os ;
|
||||
import sequence ;
|
||||
import path ;
|
||||
|
||||
project /boost/mysql/test ;
|
||||
|
||||
# System libraries
|
||||
if [ os.name ] = NT
|
||||
{
|
||||
local OPENSSL_ROOT_ENV = [ os.environ OPENSSL_ROOT ] ;
|
||||
local OPENSSL_ROOT = "" ;
|
||||
if $(OPENSSL_ROOT_ENV)
|
||||
{
|
||||
OPENSSL_ROOT = $(OPENSSL_ROOT_ENV) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
OPENSSL_ROOT = "C:/OpenSSL" ;
|
||||
}
|
||||
local openssl_requirements =
|
||||
<include>$(OPENSSL_ROOT)/include
|
||||
<library-path>$(OPENSSL_ROOT)/lib
|
||||
;
|
||||
|
||||
if [ path.exists $(OPENSSL_ROOT)/lib/libssl.lib ]
|
||||
{
|
||||
echo "OpenSSL > 1.1.0. Including libssl" ;
|
||||
lib ssl : : <target-os>windows <name>libssl : : $(openssl_requirements) ;
|
||||
}
|
||||
else if [ path.exists $(OPENSSL_ROOT)/lib/ssleay32.lib ]
|
||||
{
|
||||
echo "OpenSSL < 1.1.0. Including ssleay32" ;
|
||||
lib ssl : : <target-os>windows <name>ssleay32 : : $(openssl_requirements) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
lib ssl : : <link>shared : : $(openssl_requirements) ;
|
||||
}
|
||||
|
||||
if [ path.exists $(OPENSSL_ROOT)/lib/libcrypto.lib ]
|
||||
{
|
||||
echo "OpenSSL > 1.1.0. Including libcrypto" ;
|
||||
lib crypto : : <target-os>windows <name>libcrypto : : $(openssl_requirements) ;
|
||||
}
|
||||
else if [ path.exists $(OPENSSL_ROOT)/lib/libeay32.lib ]
|
||||
{
|
||||
echo "OpenSSL < 1.1.0. Including libeay32" ;
|
||||
lib crypto : : <target-os>windows <name>libeay32 : : $(openssl_requirements) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
lib crypto : : <link>shared : : $(openssl_requirements) ;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
local OPENSSL_ROOT = [ os.environ OPENSSL_ROOT ] ;
|
||||
local openssl_requirements =
|
||||
<include>$(OPENSSL_ROOT)/include
|
||||
<library-path>$(OPENSSL_ROOT)/lib
|
||||
;
|
||||
lib ssl : : <link>shared : : $(openssl_requirements) ;
|
||||
lib crypto : : <link>shared : : $(openssl_requirements) ;
|
||||
}
|
||||
|
||||
# Requirements to use across targets
|
||||
local requirements =
|
||||
<include>../include
|
||||
<define>BOOST_ALL_NO_LIB=1
|
||||
<define>BOOST_ASIO_NO_DEPRECATED=1
|
||||
<define>BOOST_ASIO_DISABLE_BOOST_ARRAY=1
|
||||
<define>BOOST_ASIO_DISABLE_BOOST_BIND=1
|
||||
<define>BOOST_ASIO_DISABLE_BOOST_DATE_TIME=1
|
||||
<define>BOOST_ASIO_DISABLE_BOOST_REGEX=1
|
||||
<define>BOOST_ASIO_HAS_DEFAULT_FUNCTION_TEMPLATE_ARGUMENTS=1
|
||||
<define>BOOST_COROUTINES_NO_DEPRECATION_WARNING=1
|
||||
<define>BOOST_ALLOW_DEPRECATED_HEADERS=1
|
||||
<toolset>msvc:<cxxflags>"/bigobj /Zc:__cplusplus"
|
||||
<toolset>msvc-14.1:<cxxflags>"/permissive-"
|
||||
<toolset>msvc-14.2:<cxxflags>"/permissive-"
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1
|
||||
<toolset>msvc:<define>_SILENCE_CXX17_ALLOCATOR_VOID_DEPRECATION_WARNING
|
||||
<toolset>msvc:<define>_SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING
|
||||
<toolset>msvc:<define>_SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING
|
||||
<target-os>linux:<define>_XOPEN_SOURCE=600
|
||||
<target-os>linux:<define>_GNU_SOURCE=1
|
||||
<target-os>windows:<define>_WIN32_WINNT=0x0601
|
||||
;
|
||||
|
||||
# A static Asio library to speed up builds
|
||||
lib asio_separate_build
|
||||
:
|
||||
../tools/asio.cpp
|
||||
ssl
|
||||
crypto
|
||||
:
|
||||
<link>static
|
||||
$(requirements)
|
||||
<define>BOOST_ASIO_SEPARATE_COMPILATION
|
||||
:
|
||||
:
|
||||
$(requirements)
|
||||
<define>BOOST_ASIO_SEPARATE_COMPILATION
|
||||
;
|
||||
|
||||
alias mysql : asio_separate_build ;
|
||||
|
||||
alias test_common
|
||||
:
|
||||
/boost/mysql//mysql
|
||||
mysql
|
||||
/boost/test//boost_unit_test_framework
|
||||
:
|
||||
<link>static
|
||||
|
@ -18,139 +121,3 @@ alias test_common
|
|||
:
|
||||
<include>common
|
||||
;
|
||||
|
||||
|
||||
# Unit tests
|
||||
lib unittests_entry_point
|
||||
:
|
||||
unit/entry_point.cpp
|
||||
test_common
|
||||
:
|
||||
<link>static
|
||||
;
|
||||
|
||||
cpp-pch unittests_pch
|
||||
:
|
||||
unit/pch.hpp
|
||||
unittests_entry_point
|
||||
:
|
||||
<include>unit
|
||||
;
|
||||
unit-test boost_mysql_unittests
|
||||
:
|
||||
unittests_pch
|
||||
unittests_entry_point
|
||||
|
||||
unit/detail/channel/read_buffer.cpp
|
||||
unit/detail/channel/message_parser.cpp
|
||||
unit/detail/channel/message_reader.cpp
|
||||
unit/detail/channel/message_writer_processor.cpp
|
||||
unit/detail/channel/message_writer.cpp
|
||||
unit/detail/auth/auth_calculator.cpp
|
||||
unit/detail/auxiliar/static_string.cpp
|
||||
unit/detail/auxiliar/rows_iterator.cpp
|
||||
unit/detail/auxiliar/field_type_traits.cpp
|
||||
unit/detail/auxiliar/datetime.cpp
|
||||
unit/detail/network_algorithms/read_one_row.cpp
|
||||
unit/detail/network_algorithms/read_some_rows.cpp
|
||||
unit/detail/network_algorithms/read_all_rows.cpp
|
||||
unit/detail/protocol/capabilities.cpp
|
||||
unit/detail/protocol/null_bitmap_traits.cpp
|
||||
unit/detail/protocol/serialization_test.cpp
|
||||
unit/detail/protocol/deserialize_text_field.cpp
|
||||
unit/detail/protocol/deserialize_binary_field.cpp
|
||||
unit/detail/protocol/deserialize_row.cpp
|
||||
unit/date.cpp
|
||||
unit/datetime.cpp
|
||||
unit/field_view.cpp
|
||||
unit/field.cpp
|
||||
unit/row_view.cpp
|
||||
unit/row.cpp
|
||||
unit/rows_view.cpp
|
||||
unit/rows.cpp
|
||||
unit/metadata.cpp
|
||||
unit/metadata_collection_view.cpp
|
||||
unit/error.cpp
|
||||
unit/statement.cpp
|
||||
unit/resultset.cpp
|
||||
unit/regressions.cpp
|
||||
unit/connection.cpp
|
||||
:
|
||||
<toolset>msvc:<cxxflags>-FI"pch.hpp" # https://github.com/boostorg/boost/issues/711
|
||||
;
|
||||
|
||||
|
||||
# Integration test filtering
|
||||
local test_exclusions = "" ;
|
||||
if [ os.environ BOOST_MYSQL_NO_UNIX_SOCKET_TESTS ] != "" {
|
||||
test_exclusions += "!@unix" ;
|
||||
}
|
||||
if [ os.environ BOOST_MYSQL_NO_SHA256_TESTS ] != "" {
|
||||
test_exclusions += "!@sha256" ;
|
||||
}
|
||||
|
||||
local test_filter = [ sequence.join $(test_exclusions) : ":" ] ;
|
||||
|
||||
local test_command = "" ;
|
||||
if $(test_filter) != "" {
|
||||
test_command = "-t $(test_filter)" ;
|
||||
}
|
||||
|
||||
# Integration tests
|
||||
lib integrationtests_entry_point :
|
||||
integration/entry_point.cpp
|
||||
test_common
|
||||
:
|
||||
<link>static
|
||||
;
|
||||
|
||||
cpp-pch integrationtests_pch
|
||||
:
|
||||
integration/pch.hpp
|
||||
/boost/coroutine//boost_coroutine
|
||||
integrationtests_entry_point
|
||||
:
|
||||
<include>integration
|
||||
;
|
||||
|
||||
unit-test boost_mysql_integrationtests
|
||||
:
|
||||
integrationtests_pch
|
||||
/boost/coroutine//boost_coroutine
|
||||
integrationtests_entry_point
|
||||
|
||||
# Utilities
|
||||
integration/utils/src/get_endpoint.cpp
|
||||
integration/utils/src/metadata_validator.cpp
|
||||
integration/utils/src/network_result.cpp
|
||||
integration/utils/src/er_network_variant.cpp
|
||||
integration/utils/src/sync_errc.cpp
|
||||
integration/utils/src/sync_exc.cpp
|
||||
integration/utils/src/async_callback.cpp
|
||||
integration/utils/src/async_callback_noerrinfo.cpp
|
||||
integration/utils/src/async_future.cpp
|
||||
integration/utils/src/async_coroutine.cpp
|
||||
integration/utils/src/async_coroutinecpp20.cpp
|
||||
integration/utils/src/default_completion_tokens.cpp
|
||||
|
||||
# Actual tests
|
||||
integration/connection.cpp
|
||||
integration/connect.cpp
|
||||
integration/handshake.cpp
|
||||
integration/query.cpp
|
||||
integration/prepare_statement.cpp
|
||||
integration/execute_statement.cpp
|
||||
integration/close_statement.cpp
|
||||
integration/statement_lifecycle.cpp
|
||||
integration/resultset.cpp
|
||||
integration/quit_connection.cpp
|
||||
integration/close_connection.cpp
|
||||
integration/reconnect.cpp
|
||||
integration/database_types.cpp
|
||||
:
|
||||
<testing.arg>$(test_command)
|
||||
<include>integration/utils/include
|
||||
<toolset>msvc:<cxxflags>-FI"pch.hpp" # https://github.com/boostorg/boost/issues/711
|
||||
;
|
||||
|
||||
explicit boost_mysql_integrationtests ;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue