Generic execute and start_execution functions

Added execute, start_execution, ExecutionRequest and
    statement::bind. Deprecated query, execute_statement,
    start_query and start_statement_execution.
Statements can now be executed specifying parameters
    as iterator ranges.
Fixed a bug that caused build problems when FieldViewForwardIterator's
    reference is convertible to field_view, but not field_view.

Close #111
Close #137
Close #138
This commit is contained in:
Ruben Perez 2023-04-03 16:31:33 +02:00
parent ed007e31ae
commit 0571e4b886
67 changed files with 1748 additions and 678 deletions

View File

@ -47,6 +47,7 @@ docca.reference reference.qbk
\"BOOST_CXX14_CONSTEXPR=constexpr\" \\
\"BOOST_MYSQL_FIELD_LIKE_TUPLE=class\" \\
\"BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR=class\" \\
\"BOOST_MYSQL_EXECUTION_REQUEST=class\" \\
"
<doxygen:param>SKIP_FUNCTION_MACROS=NO
<doxygen:param>OUTPUT_LANGUAGE=English

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 29 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -40,6 +40,7 @@
[def __CompletionToken__ [@boost:/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.completion_tokens_and_handlers ['CompletionToken]]]
[def __FieldViewFwdIterator__ [reflink2 FieldViewFwdIterator ['FieldViewFwdIterator]]]
[def __FieldLikeTuple__ [reflink2 FieldLikeTuple ['FieldLikeTuple]]]
[def __ExecutionRequest__ [reflink2 ExecutionRequest ['ExecutionRequest]]]
[def __Boost__ [@https://www.boost.org/ Boost]]
[def __Asio__ [@boost:/libs/asio/index.html Boost.Asio]]
@ -115,5 +116,6 @@
[include helpers/SocketStream.qbk]
[include helpers/FieldViewFwdIterator.qbk]
[include helpers/FieldLikeTuple.qbk]
[include helpers/ExecutionRequest.qbk]
[block'''</part>''']
[endsect]

View File

@ -62,7 +62,7 @@ which accepts two parameters:
[heading Issuing the SQL query]
The next step is to issue the query to the server. We will use
[reflink2 connection.query tcp_ssl_connection::query],
[reflink2 connection.execute tcp_ssl_connection::execute],
which accepts a string containing a single SQL query and instructs
the server to run it. It returns a [reflink results]
object, containing the rows returned by the query:

View File

@ -57,7 +57,8 @@ read [link mysql.other_streams.non_sockets this section] for more info.
[section:queries_stmts Text queries and prepared statements]
The two main ways to use a connection are text queries and prepared statements:
The two main ways to use a connection are text queries and prepared statements.
You can access both using [refmem connection execute]:
[table
[
@ -67,7 +68,7 @@ The two main ways to use a connection are text queries and prepared statements:
]
[
[
Text queries: [refmem connection query].
Text queries
]
[
Simple queries, without parameters:
@ -83,9 +84,7 @@ The two main ways to use a connection are text queries and prepared statements:
]
[
[
Prepared statements:[br]
[refmem connection prepare_statement][br]
[refmem connection execute_statement]
Prepared statements
]
[
Queries with parameters unknown at compile-time.
@ -117,8 +116,8 @@ We can see that a resultset is composed of three pieces of information:
* Additional information about the query execution, like the number of affected rows ([refmem results affected_rows])
or the number of warnings generated by the query ([refmem results warning_count]).
You can obtain a `results` object by executing a text query ([refmem connection query]) or a prepared statement
([refmem connection execute_statement]).
You can obtain a `results` object by executing a text query or a prepared statement, by calling
[refmem connection 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.
@ -309,7 +308,7 @@ Until now, we've used simple text queries that did not contain any user-provided
In the 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
and then pass it to `execute()`. 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
@ -321,17 +320,16 @@ by ID. You've got the following table definition:
[overview_statements_setup]
You can prepare a statement to retrieve products by ID using:
You can prepare a statement to retrieve products by ID using [refmem connection prepare_statement]:
[overview_statements_prepare]
You can execute the statement using [refmem connection execute_statement]:
You can execute the statement using [refmem connection execute]:
[overview_statements_execute]
The `statement` object is passed as first parameter, which tells the server which
statement it should execute. Actual parameters are passed as the second argument,
as a `std::tuple`. You must pass as many parameters as `?` placeholders the statement has.
We used [refmem statement bind] to provide actual parameters to the statement.
You must pass as many parameters to `bind` as `?` placeholders the statement has.
To learn more about prepared statements, please refer to [link mysql.prepared_statements this section].
@ -339,8 +337,8 @@ To learn more about prepared statements, please refer to [link mysql.prepared_st
[section Multi-function operations]
Until now, we've been using [refmem connection query] and [refmem connection execute_statement], which
execute some SQL and read all generated data into an in-memory `results` object.
Until now, we've been using [refmem connection execute], which
executes some SQL and reads all generated data into an in-memory `results` object.
Some use cases may not fit in this simple pattern. For example:
@ -349,14 +347,14 @@ Some use cases may not fit in this simple pattern. For example:
* If rows contain very long `TEXT` or `BLOB` fields, it may not be adequate to copy these values
from the network buffer into the `results` object. A view-based approach may be better.
For these cases, we can break the `query()` or `execute_statement()` operation into several steps,
For these cases, we can break the `execute()` operation into several steps,
using a ['multi-function operation] (the term is coined by this library). This example reads an entire
table in batches, which can be the case in an ETL process:
[overview_multifn]
[warning
Once you start a multi-function operation with [refmem connection start_query] or [refmem connection start_statement_execution],
Once you start a multi-function operation with [refmem connection 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!
]
@ -404,8 +402,7 @@ This is because the connection uses the underlying `Stream` object directly, wit
or queueing. If you perform several async operations concurrently on a single connection without any
serialization, the stream may interleave reads and writes from different operations, leading to undefined behavior.
For example, [refmem connection async_query] performs both reads and writes.
Doing this is illegal and should be avoided:
For example, doing the following is illegal and should be avoided:
[overview_async_dont]

View File

@ -7,12 +7,13 @@
[section:queries Text queries]
To run a text query, use any of the following functions:
To run a text query, use any of the following functions, passing a string-like
object (convertible to [reflink string_view]) containing valid SQL as the first parameter:
* [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.
* [refmem connection execute] or [refmem connection async_execute]: these functions run the query and
read the generated results into memory.
* [refmem connection start_execution] and [refmem connection async_start_execution]: these functions
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,
@ -36,7 +37,7 @@ instead, which perform composition server-side in a safe way.
[heading Running multiple queries at once]
You can run several semicolon-separated queries in a single call by enabling
You can run several semicolon-separated queries in a single `execute()` call by enabling
the [refmem handshake_params multi_queries] option. You can find an example
[link mysql.multi_resultset.multi_queries here].

View File

@ -29,9 +29,9 @@ will use these values to run the statement.
To execute a statement, use any of the following functions:
* [refmem connection execute_statement] or [refmem connection async_execute_statement],
* [refmem connection execute] or [refmem connection async_execute],
which execute the statement and read the generated rows.
* [refmem connection start_statement_execution] and [refmem connection async_start_statement_execution], which initiate a
* [refmem connection start_execution] and [refmem connection async_start_execution], which initiate a
statement execution as a multi-function operation.
For example:
@ -47,9 +47,9 @@ Some observations:
* 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_statement()`, but is defined as a `TINYINT`
* `show_in_store` is passed as an `int` to `statement::bind()`, 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_statement()` will fail with an error
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
@ -60,6 +60,14 @@ to insert `NULL` values:
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 Executing a statement with a variable number of parameters]
The above approach works when you know at compile time how many parameters the statement has.
In some scenarios (e.g. a graphical interface), this may not be the case. For these cases, you can
`bind` a statement to a `field` or `field_view` iterator range:
[prepared_statements_execute_iterator_range]
[heading Closing a statement]
Prepared statements are created server-side, and thus consume server resources. If you don't need a

View File

@ -86,7 +86,7 @@ To simplify things, you can use [refmem results out_params] to retrieve them:
[heading:multi_queries Semicolon-separated queries]
It is possible to run several semicolon-separated text queries in a single [refmem connection query] call.
It is possible to run several semicolon-separated text queries in a single [refmem connection execute] call.
For security, this capability is disabled by default. Enabling it requires setting [refmem handshake_params multi_queries]
before connecting:

View File

@ -28,9 +28,6 @@ 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:
@ -43,8 +40,8 @@ There are two separate cases:
* 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 connection execute_statement] handle the full message exchange. In contrast,
[refmem connection start_query] and [refmem connection start_statement_execution] will not read the rows, if any.
[refmem connection execute] handles the full message exchange. In contrast,
[refmem connection start_execution] will not read the rows, if any.
Some takeaways:
@ -63,8 +60,7 @@ Given the following setup:
[multi_function_setup]
You can start a multi-function operation using [refmem connection start_query]
or [refmem connection start_statement_execution]:
You can start a multi-function operation using [refmem connection start_execution]:
[table
[
@ -72,8 +68,8 @@ or [refmem connection start_statement_execution]:
[Prepared statements]
]
[
[[multi_function_start_query]]
[[multi_function_start_statement_execution]]
[[multi_function_text_queries]]
[[multi_function_statements]]
]
]
@ -138,7 +134,7 @@ while `should_read_rows()` will return false once an individual result has been
describes which reading function should be invoked next:
* [refmem execution_state should_start_op]: the initial state, after you default-construct an `execution_state`.
You should call functions like `start_query` or `start_statement_execution` to start the operation.
You should call [refmem connection start_execution] or [refmem connection async_start_execution] to start the operation.
* [refmem execution_state should_read_rows]: the next operation should be [refmem connection read_some_rows],
to read the generated rows.
* [refmem execution_state should_read_head]: the next operation should be [refmem connection read_resultset_head],

View File

@ -492,7 +492,7 @@ column shows what [refmem metadata type] would return for a column of that type.
[section:cpp_to_mysql C++ to MySQL type mapping reference]
This section shows how a parameter `v` in a expression `conn.execute_statement(stmt, std::make_tuple(v), result)`
This section shows how a parameter `v` in a expression `conn.execute(stmt.bind(v), result)`
is interpreted by MySQL, depeding on `v`'s type:
[table

View File

@ -46,13 +46,9 @@ character set and collation. You can specify this in two ways:
* At any time, issuing a __SET_NAMES__ SQL statement. For example, `"SET NAMES utf8mb4"` will set the current
connection's character set to `utf8mb4` and the connection's collation to utf8mb4's default collation.
If the character set is unknown, the `SET NAMES` statement will fail.
You can use [refmem connection query] to issue the statement:
You can use [refmem connection execute] to issue the statement:
```
results result;
conn.query("SET NAMES utf8mb4", result);
// Further operations can assume utf8mb4 as conn's charset
```
[charsets_set_names]
[endsect]
@ -63,14 +59,14 @@ The ones that impact this library's behavior are:
* [mysqllink server-system-variables.html#sysvar_character_set_client character_set_client]
determines the encoding that SQL statements sent to the server should have. This includes
the SQL strings in [refmem connection query], [refmem connection prepare_statement], and
string parameters passed to [refmem connection execute_statement].
the SQL strings passed to [refmem connection execute] and [refmem connection prepare_statement], and
string parameters passed to [refmem statement bind].
Not all character sets are permissible in `character_set_client`. The server will accept setting
this variable to any UTF-8 character set, but won't accept UTF-16.
* [mysqllink server-system-variables.html#sysvar_character_set_results character_set_results]
determines the encoding that the server will use to send any kind of result, including
string fields retrieved by [refmem connection query] and [refmem connection execute_statement], metadata
string fields retrieved by [refmem connection execute], metadata
like [refmem metadata column_name] and error messages.
Note that, when you define a string column with a character set (e.g.
@ -90,16 +86,16 @@ The table below summarizes the encoding used by each piece of functionality in t
[Encoding given by...]
]
[
[SQL query strings passed to [refmem connection query] and [refmem connection prepare_statement]]
[SQL query strings passed to [refmem connection execute] and [refmem connection prepare_statement]]
[`character_set_client`]
]
[
[String values passed as parameters to [refmem connection execute_statement]]
[String values passed as parameters to [refmem statement bind]]
[`character_set_client`]
]
[
[
String fields retrieved by [refmem connection query] or [refmem connection execute_statement]:[br][br]
String fields retrieved by [refmem connection execute] or [refmem connection read_some_rows]:[br][br]
[refmem field_view as_string][br]
[refmem field_view get_string]
]

View File

@ -0,0 +1,25 @@
[/
Copyright (c) 2019-2023 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:boost__mysql__ExecutionRequest ExecutionRequest concept]
An execution request represents a SQL statement to be executed by the
server, plus any parameters required to run the query. It may model
a plain text query, or a prepared statement handle with bound parameters.
Formally, a type `T` is a `ExecutionRequest` if it fulfills any of the following:
* It is convertible to [reflink string_view]. In this case, the execution request
contains a text query to be run by the server.
* An instantiation of the [reflink bound_statement_tuple] class, or a (possibly cv-qualified)
reference to it.
* An instantiation of the [reflink bound_statement_iterator_range] class, or a (possibly cv-qualified)
reference to it.
This definition may be extended in future versions, but the above types will still satisfy `ExecutionRequest`.
[endsect]

View File

@ -16,6 +16,8 @@
<bridgehead renderas="sect3">Classes</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="mysql.ref.boost__mysql__bad_field_access">bad_field_access</link></member>
<member><link linkend="mysql.ref.boost__mysql__bound_statement_tuple">bound_statement_tuple</link></member>
<member><link linkend="mysql.ref.boost__mysql__bound_statement_iterator_range">bound_statement_iterator_range</link></member>
<member><link linkend="mysql.ref.boost__mysql__buffer_params">buffer_params</link></member>
<member><link linkend="mysql.ref.boost__mysql__connection">connection</link></member>
<member><link linkend="mysql.ref.boost__mysql__date">date</link></member>
@ -87,6 +89,7 @@
</simplelist>
<bridgehead renderas="sect3">Concepts</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="mysql.ref.boost__mysql__ExecutionRequest">ExecutionRequest</link></member>
<member><link linkend="mysql.ref.boost__mysql__FieldLikeTuple">FieldLikeTuple</link></member>
<member><link linkend="mysql.ref.boost__mysql__FieldViewFwdIterator">FieldViewFwdIterator</link></member>
<member><link linkend="mysql.ref.boost__mysql__SocketStream">SocketStream</link></member>

View File

@ -7,9 +7,7 @@
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
expand-text="yes">
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" expand-text="yes">
<xsl:variable name="doc-ns" select="'boost::mysql'"/>
<xsl:variable name="doc-ref" select="'mysql.ref'"/>
@ -36,7 +34,8 @@
'SocketStream',
'Executor',
'FieldLikeTuple',
'FieldViewFwdIterator'
'FieldViewFwdIterator',
'ExecutionRequest'
"/>
</xsl:stylesheet>

View File

@ -85,7 +85,7 @@ public:
void query_employees()
{
conn.async_execute_statement(stmt, std::make_tuple(company_id), result, diag, [this](error_code err) {
conn.async_execute(stmt.bind(company_id), result, diag, [this](error_code err) {
boost::mysql::throw_on_error(err, diag);
for (boost::mysql::row_view employee : result.rows())
{

View File

@ -90,7 +90,7 @@ void main_impl(int argc, char** argv)
// Execute the statement
boost::mysql::results result;
conn.async_execute_statement(stmt, std::make_tuple(company_id), result, diag, yield[ec]);
conn.async_execute(stmt.bind(company_id), result, diag, yield[ec]);
boost::mysql::throw_on_error(ec, diag);
// Print the employees

View File

@ -91,9 +91,7 @@ boost::asio::awaitable<void> coro_main(
// Execute the statement
boost::mysql::results result;
std::tie(ec
) = co_await conn
.async_execute_statement(stmt, std::make_tuple(company_id), result, diag, tuple_awaitable);
std::tie(ec) = co_await conn.async_execute(stmt.bind(company_id), result, diag, tuple_awaitable);
boost::mysql::throw_on_error(ec, diag);
// Print all employees

View File

@ -109,7 +109,7 @@ void main_impl(int argc, char** argv)
// Execute the statement
boost::mysql::results result;
fut = conn.async_execute_statement(stmt, std::make_tuple(company_id), result, use_future);
fut = conn.async_execute(stmt.bind(company_id), result, use_future);
fut.get();
// Print employees

View File

@ -69,7 +69,7 @@ boost::asio::awaitable<void> coro_main(
// Execute it
boost::mysql::results result;
std::tie(ec) = co_await conn.async_execute_statement(stmt, std::make_tuple(company_id), result, diag);
std::tie(ec) = co_await conn.async_execute(stmt.bind(company_id), result, diag);
boost::mysql::throw_on_error(ec, diag);
// Use the received rows

View File

@ -59,7 +59,7 @@ void main_impl(int argc, char** argv)
JOIN company comp ON (comp.id = emp.company_id)
)";
boost::mysql::results result;
conn.query(sql, result);
conn.execute(sql, result);
/**
* results objects allow you to access metadata about the columns in the query

View File

@ -95,12 +95,13 @@ void main_impl(int argc, char** argv)
/*
* Once a statement has been prepared, it can be executed by calling
* connection::execute_statement(). Parameter actual values are provided
* as a std::tuple. Executing a statement yields a results object.
* connection::execute(). Parameters are provided to statement::bind(),
* which creates a bound statement object that can be passed to execute().
* Executing a statement yields a results object.
*/
//[prepared_statements_execute
boost::mysql::results select_result, update_result;
conn.execute_statement(salary_getter, std::make_tuple(first_name), select_result);
conn.execute(salary_getter.bind(first_name), select_result);
//]
// First row, first column, cast to double
@ -109,7 +110,7 @@ void main_impl(int argc, char** argv)
// Run the update. In this case, we must pass in two parameters.
double payrise = generate_random_payrise();
conn.execute_statement(salary_updater, std::make_tuple(payrise, first_name), update_result);
conn.execute(salary_updater.bind(payrise, first_name), update_result);
ASSERT(update_result.rows().empty()); // an UPDATE never returns rows
/**
@ -117,7 +118,7 @@ void main_impl(int argc, char** argv)
* as many times as we want. We do NOT need to call
* connection::prepare_statement() again.
*/
conn.execute_statement(salary_getter, std::make_tuple(first_name), select_result);
conn.execute(salary_getter.bind(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;

View File

@ -91,11 +91,7 @@ void insert_product(
)
{
results result;
conn.execute_statement(
stmt,
std::make_tuple(description, price, static_cast<int>(show_in_store)),
result
);
conn.execute(stmt.bind(description, price, static_cast<int>(show_in_store)), result);
}
//]
@ -117,15 +113,19 @@ void insert_product(
// Execute the insert
results result;
conn.execute_statement(
stmt,
std::make_tuple(description_param, price, static_cast<int>(show_in_store)),
result
);
conn.execute(stmt.bind(description_param, price, static_cast<int>(show_in_store)), result);
}
//]
#endif
//[prepared_statements_execute_iterator_range
void exec_statement(tcp_ssl_connection& conn, const statement& stmt, const std::vector<field>& params)
{
results result;
conn.execute(stmt.bind(params.begin(), params.end()), result);
}
//]
#ifdef BOOST_ASIO_HAS_CO_AWAIT
boost::asio::awaitable<void> overview_coro(tcp_ssl_connection& conn)
{
@ -137,7 +137,7 @@ boost::asio::awaitable<void> overview_coro(tcp_ssl_connection& conn)
// Run our query as a coroutine
diagnostics diag;
results result;
auto [ec] = co_await conn.async_query("SELECT 'Hello world!'", result, diag, token);
auto [ec] = co_await conn.async_execute("SELECT 'Hello world!'", result, diag, token);
// This will throw an error_with_diagnostics in case of failure
boost::mysql::throw_on_error(ec, diag);
@ -172,8 +172,8 @@ boost::asio::awaitable<void> dont_run()
// DO NOT DO THIS!!!!
results result1, result2;
co_await (
conn.async_query("SELECT 1", result1, boost::asio::use_awaitable) &&
conn.async_query("SELECT 2", result2, boost::asio::use_awaitable)
conn.async_execute("SELECT 1", result1, boost::asio::use_awaitable) &&
conn.async_execute("SELECT 2", result2, boost::asio::use_awaitable)
);
//]
}
@ -218,7 +218,7 @@ void main_impl(int argc, char** argv)
{
//[overview_query_use_case
results result;
conn.query("START TRANSACTION", result);
conn.execute("START TRANSACTION", result);
//]
}
{
@ -228,14 +228,14 @@ void main_impl(int argc, char** argv)
);
results result;
conn.execute_statement(stmt, std::make_tuple("HGS", 30000), result);
conn.execute(stmt.bind("HGS", 30000), result);
//]
}
{
//[overview_views
// Populate a results object
results result;
conn.query("SELECT 'Hello world'", result);
conn.execute("SELECT 'Hello world'", result);
// results::rows() returns a rows_view. The underlying memory is owned by the results object
rows_view all_rows = result.rows();
@ -263,7 +263,7 @@ void main_impl(int argc, char** argv)
{
//[overview_using_fields
results result;
conn.query("SELECT 'abc', 42", result);
conn.execute("SELECT 'abc', 42", result);
// Obtain a field's underlying value using the is_xxx and get_xxx accessors
field_view f = result.rows().at(0).at(0); // f points to the string "abc"
@ -290,7 +290,7 @@ void main_impl(int argc, char** argv)
results result;
// Create some test data
conn.query(
conn.execute(
R"%(
CREATE TEMPORARY TABLE products (
id VARCHAR(50) PRIMARY KEY,
@ -299,10 +299,10 @@ void main_impl(int argc, char** argv)
)%",
result
);
conn.query("INSERT INTO products VALUES ('PTT', 'Potatoes'), ('CAR', NULL)", result);
conn.execute("INSERT INTO products VALUES ('PTT', 'Potatoes'), ('CAR', NULL)", result);
// Retrieve the data. Note that some fields are NULL
conn.query("SELECT id, description FROM products", result);
conn.execute("SELECT id, description FROM products", result);
for (row_view r : result.rows())
{
@ -327,12 +327,12 @@ void main_impl(int argc, char** argv)
}
//]
conn.query("DROP TABLE products", result);
conn.execute("DROP TABLE products", result);
}
{
//[overview_statements_setup
results result;
conn.query(
conn.execute(
R"%(
CREATE TEMPORARY TABLE products (
id VARCHAR(50) PRIMARY KEY,
@ -341,7 +341,7 @@ void main_impl(int argc, char** argv)
)%",
result
);
conn.query("INSERT INTO products VALUES ('PTT', 'Potatoes'), ('CAR', 'Carrots')", result);
conn.execute("INSERT INTO products VALUES ('PTT', 'Potatoes'), ('CAR', 'Carrots')", result);
//]
}
{
@ -355,19 +355,19 @@ void main_impl(int argc, char** argv)
// Execute the statement
results result;
conn.execute_statement(stmt, std::make_tuple(product_id), result);
conn.execute(stmt.bind(product_id), result);
// Use result as required
//]
conn.query("DROP TABLE products", result);
conn.execute("DROP TABLE products", result);
}
{
//[overview_multifn
// Create the table and some sample data
// In a real system, body may be megabaytes long.
results result;
conn.query(
conn.execute(
R"%(
CREATE TEMPORARY TABLE posts (
id INT PRIMARY KEY AUTO_INCREMENT,
@ -377,7 +377,7 @@ void main_impl(int argc, char** argv)
)%",
result
);
conn.query(
conn.execute(
R"%(
INSERT INTO posts (title, body) VALUES
('Post 1', 'A very long post body'),
@ -390,7 +390,7 @@ void main_impl(int argc, char** argv)
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);
conn.start_execution("SELECT title, body FROM posts", st);
// Reads all the returned rows, in batches.
// st.complete() returns true once there are no more rows to read
@ -407,7 +407,7 @@ void main_impl(int argc, char** argv)
}
//]
conn.query("DROP TABLE posts", result);
conn.execute("DROP TABLE posts", result);
}
{
//[overview_errors_sync_errc
@ -417,7 +417,7 @@ void main_impl(int argc, char** argv)
// The provided SQL is invalid. The server will return an error.
// ec will be set to a non-zero value
conn.query("this is not SQL!", result, ec, diag);
conn.execute("this is not SQL!", result, ec, diag);
if (ec)
{
@ -437,7 +437,7 @@ void main_impl(int argc, char** argv)
{
// The provided SQL is invalid. This function will throw an exception.
results result;
conn.query("this is not SQL!", result);
conn.execute("this is not SQL!", result);
}
catch (const error_with_diagnostics& err)
{
@ -459,7 +459,7 @@ void main_impl(int argc, char** argv)
//[prepared_statements_prepare
// Table setup
results result;
conn.query(
conn.execute(
R"%(
CREATE TEMPORARY TABLE products (
id INT PRIMARY KEY AUTO_INCREMENT,
@ -482,16 +482,17 @@ void main_impl(int argc, char** argv)
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
insert_product(conn, stmt, std::optional<string_view>(), 2000, true);
#endif
conn.query("DROP TABLE products", result);
exec_statement(conn, stmt, {field_view("abc"), field_view(2000), field_view(1)});
conn.execute("DROP TABLE products", result);
}
// multi-resultset
{
results result;
conn.query("DROP PROCEDURE IF EXISTS get_employee", result);
conn.execute("DROP PROCEDURE IF EXISTS get_employee", result);
//[multi_resultset_procedure
conn.query(
conn.execute(
R"(
CREATE PROCEDURE get_employee(IN pin_employee_id INT)
BEGIN
@ -511,7 +512,7 @@ void main_impl(int argc, char** argv)
std::int64_t employee_id = get_employee_id();
// Call the statement
conn.execute_statement(get_employee_stmt, std::make_tuple(employee_id), result);
conn.execute(get_employee_stmt.bind(employee_id), result);
//]
//[multi_resultset_first_resultset
@ -523,11 +524,11 @@ void main_impl(int argc, char** argv)
}
{
results result;
conn.query("DROP PROCEDURE IF EXISTS create_employee", result);
conn.execute("DROP PROCEDURE IF EXISTS create_employee", result);
//[multi_resultset_out_params
// Setup the stored procedure
conn.query(
conn.execute(
R"(
CREATE PROCEDURE create_employee(
IN pin_company_id CHAR(10),
@ -554,7 +555,7 @@ void main_impl(int argc, char** argv)
// When executing the statement, we provide an actual value for the IN parameters,
// and a dummy value for the OUT parameter. This value will be ignored, but it's required by the
// protocol
conn.execute_statement(stmt, std::make_tuple("HGS", "John", "Doe", nullptr), result);
conn.execute(stmt.bind("HGS", "John", "Doe", nullptr), result);
// Retrieve output parameters. This row_view has an element per
// OUT or INOUT parameter that used a ? placeholder
@ -586,7 +587,7 @@ void main_impl(int argc, char** argv)
// We can now use the multi-query feature.
// This will result in three resultsets, one per query.
results result;
conn.query(
conn.execute(
R"(
CREATE TEMPORARY TABLE posts (
id INT PRIMARY KEY AUTO_INCREMENT,
@ -627,7 +628,7 @@ void main_impl(int argc, char** argv)
{
//[multi_function_setup
results result;
conn.query(
conn.execute(
R"%(
CREATE TEMPORARY TABLE posts (
id INT PRIMARY KEY AUTO_INCREMENT,
@ -637,7 +638,7 @@ void main_impl(int argc, char** argv)
)%",
result
);
conn.query(
conn.execute(
R"%(
INSERT INTO posts (title, body) VALUES
('Post 1', 'A very long post body'),
@ -667,35 +668,34 @@ void main_impl(int argc, char** argv)
};
{
//[multi_function_start_query
//[multi_function_text_queries
execution_state st;
conn.start_query("SELECT title, body FROM posts", st);
conn.start_execution("SELECT title, body FROM posts", st);
//]
read_all_rows(st); // don't compromise further operations
}
{
//[multi_function_start_statement_execution
//[multi_function_statements
execution_state st;
conn.start_statement_execution(
stmt,
std::make_tuple(), // The statement has no params, so an empty tuple is passed
conn.start_execution(
stmt.bind(), // The statement has no params, so an empty bind is performed
st
);
//]
read_all_rows(st); // don't compromise further operations
conn.query("DROP TABLE posts", result);
conn.execute("DROP TABLE posts", result);
}
{
results result;
conn.query("DROP PROCEDURE IF EXISTS get_company", result);
conn.execute("DROP PROCEDURE IF EXISTS get_company", result);
//[multi_function_stored_procedure
// Setup the stored procedure
conn.query(
conn.execute(
R"(
CREATE PROCEDURE get_company(IN pin_company_id CHAR(10))
BEGIN
@ -714,7 +714,7 @@ void main_impl(int argc, char** argv)
// Call the procedure
execution_state st;
statement stmt = conn.prepare_statement("CALL get_company(?)");
conn.start_statement_execution(stmt, std::make_tuple(company_id), st);
conn.start_execution(stmt.bind(company_id), st);
// The above code will generate 3 resultsets
// Read the 1st one, which contains the matched companies
@ -750,7 +750,7 @@ void main_impl(int argc, char** argv)
{
//[fields_field_views
results result;
conn.query("SELECT 'Hello world!'", result);
conn.execute("SELECT 'Hello world!'", result);
// fv doesn't own its memory; if result goes out of scope, fv becomes invalid
field_view fv = result.rows().at(0).at(0);
@ -764,7 +764,7 @@ void main_impl(int argc, char** argv)
{
//[fields_field_views_scalars
results result;
conn.query("SELECT 42", result);
conn.execute("SELECT 42", result);
// fv doesn't own its memory; if result goes out of scope, fv becomes invalid
field_view fv = result.rows().at(0).at(0);
@ -778,7 +778,7 @@ void main_impl(int argc, char** argv)
{
//[fields_taking_ownership
results result;
conn.query("SELECT 'Hello world!'", result);
conn.execute("SELECT 'Hello world!'", result);
// fv doesn't own its memory; if result goes out of scope, fv becomes invalid
field_view fv = result.rows().at(0).at(0);
@ -864,7 +864,7 @@ void main_impl(int argc, char** argv)
{
//[field_timestamp_setup
results result;
conn.query(
conn.execute(
R"%(
CREATE TEMPORARY TABLE events (
id INT PRIMARY KEY AUTO_INCREMENT,
@ -885,7 +885,7 @@ void main_impl(int argc, char** argv)
// This change has session scope. All operations after this query
// will now use UTC for TIMESTAMPs. Other sessions will not see the change.
// If you need to reconnect the connection, you need to run this again.
conn.query("SET @time_zone = 'UTC'", result);
conn.execute("SET @time_zone = 'UTC'", result);
//]
//[fields_timestamp_insert
@ -894,7 +894,7 @@ void main_impl(int argc, char** argv)
datetime event_timestamp = datetime::now();
// event_timestamp will be interpreted as UTC if you have run SET @time_zone
conn.execute_statement(insert_stmt, std::make_tuple(event_timestamp, "Something happened"), result);
conn.execute(insert_stmt.bind(event_timestamp, "Something happened"), result);
//]
//[fields_timestamp_select
@ -903,14 +903,14 @@ void main_impl(int argc, char** argv)
// threshold will be interpreted as UTC. The retrieved events will have their
// `t` column in UTC
conn.execute_statement(select_stmt, std::make_tuple(threshold), result);
conn.execute(select_stmt.bind(threshold), result);
//]
}
{
//[metadata
// By default, a connection has metadata_mode::minimal
results result;
conn.query("SELECT 1 AS my_field", result);
conn.execute("SELECT 1 AS my_field", result);
string_view colname = result.meta()[0].column_name();
// colname will be empty because conn.meta_mode() == metadata_mode::minimal
@ -918,11 +918,18 @@ void main_impl(int argc, char** argv)
// If you are using metadata names, set the connection's metadata_mode
conn.set_meta_mode(metadata_mode::full);
conn.query("SELECT 1 AS my_field", result);
conn.execute("SELECT 1 AS my_field", result);
colname = result.meta()[0].column_name();
ASSERT(colname == "my_field");
//]
}
{
//[charsets_set_names
results result;
conn.execute("SET NAMES utf8mb4", result);
// Further operations can assume utf8mb4 as conn's charset
//]
}
// Close
conn.close();

View File

@ -123,9 +123,9 @@ void main_impl(int argc, char** argv)
conn.connect(*endpoints.begin(), params);
// The executed commands may generate a lot of output, so we're going to
// use multi-function operations (i.e. start_query) to read it in batches.
// use multi-function operations (i.e. start_execution) to read it in batches.
boost::mysql::execution_state st;
conn.start_query(script_contents, st);
conn.start_execution(script_contents, st);
// The main read loop. Each executed command will yield a resultset.
// st.comoplete() returns true once all resultsets have been read.

View File

@ -109,7 +109,7 @@ void main_impl(int argc, char** argv)
// We can now use the connection as we would normally do.
const char* sql = "SELECT first_name, last_name, salary FROM employee";
boost::mysql::results result;
conn.query(sql, result);
conn.execute(sql, result);
for (auto employee : result.rows())
{

View File

@ -273,7 +273,7 @@ struct visitor
auto stmt = conn.prepare_statement("CALL get_products(?)");
boost::mysql::results result;
conn.execute_statement(stmt, std::make_tuple(args.search), result);
conn.execute(stmt.bind(args.search), result);
auto products = result.front();
std::cout << "Your search returned the following products:\n";
for (auto product : products.rows())
@ -289,9 +289,9 @@ struct visitor
// create-order: creates a new order
void operator()(const create_order_args&) const
{
// Since create_order doesn't have user-supplied params, we can use query()
// Since create_order doesn't have user-supplied params, we can use a text query
boost::mysql::results result;
conn.query("CALL create_order()", result);
conn.execute("CALL create_order()", result);
// Print the result to stdout. create_order() returns a resultset for
// the newly created order, with only 1 row.
@ -307,12 +307,12 @@ struct visitor
// Execute the statement
boost::mysql::results result;
conn.execute_statement(stmt, std::make_tuple(args.order_id), result);
conn.execute(stmt.bind(args.order_id), result);
// Print the result to stdout. get_order() returns a resultset for
// the retrieved order and another for the line items. If the order can't
// be found, get_order() raises an error using SIGNAL, which will make
// execute_statement() fail with an exception.
// execute() fail with an exception.
std::cout << "Retrieved order\n";
print_order_with_items(result.at(0), result.at(1));
}
@ -320,9 +320,9 @@ struct visitor
// get-orders: lists all orders
void operator()(const get_orders_args&) const
{
// Since get_orders doesn't have user-supplied params, we can use query()
// Since get_orders doesn't have user-supplied params, we can use a text query
boost::mysql::results result;
conn.query("CALL get_orders()", result);
conn.execute("CALL get_orders()", result);
// Print results to stdout. get_orders() succeeds even if no order is found.
// get_orders() only lists orders, not line items.
@ -351,11 +351,7 @@ struct visitor
// We still have to pass a value to the 4th argument, even if it's an OUT parameter.
// The value will be ignored, so we can pass nullptr.
boost::mysql::results result;
conn.execute_statement(
stmt,
std::make_tuple(args.order_id, args.product_id, args.quantity, nullptr),
result
);
conn.execute(stmt.bind(args.order_id, args.product_id, args.quantity, nullptr), result);
// We can use results::out_params() to access the extra resultset containing
// the OUT parameter
@ -374,7 +370,7 @@ struct visitor
// Run the procedure
boost::mysql::results result;
conn.execute_statement(stmt, std::make_tuple(args.line_item_id), result);
conn.execute(stmt.bind(args.line_item_id), result);
// Print results to stdout
std::cout << "Removed line item from order\n";
@ -390,7 +386,7 @@ struct visitor
// Execute the statement
boost::mysql::results result;
conn.execute_statement(stmt, std::make_tuple(args.order_id, nullptr), result);
conn.execute(stmt.bind(args.order_id, nullptr), result);
// We can use results::out_params() to access the extra resultset containing
// the OUT parameter
@ -409,7 +405,7 @@ struct visitor
// Execute the statement
boost::mysql::results result;
conn.execute_statement(stmt, std::make_tuple(args.order_id), result);
conn.execute(stmt.bind(args.order_id), result);
// Print the results to stdout
std::cout << "Completed order\n";

View File

@ -83,14 +83,14 @@ void main_impl(int argc, char** argv)
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 results object by lvalue reference.
* Resultset objects contain the retrieved rows, among other info.
* We will get all employees working for 'High Growth Startup'.
* To issue a SQL query to the database server, use tcp_ssl_connection::execute, passing
* a string with the SQL to be executed as first parameter. execute returns a results object by lvalue
* reference. 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::results result;
conn.query(sql, result);
conn.execute(sql, result);
// We can access the rows using results::rows
for (boost::mysql::row_view employee : result.rows())
@ -101,11 +101,11 @@ void main_impl(int argc, char** argv)
// We can issue any SQL statement, not only SELECTs. In this case, the returned
// results will have no fields and no rows
sql = "UPDATE employee SET salary = 10000 WHERE first_name = 'Underpaid'";
conn.query(sql, result);
conn.execute(sql, result);
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);
conn.execute("SELECT salary FROM employee WHERE first_name = 'Underpaid'", result);
double salary = result.rows().at(0).at(0).as_double();
ASSERT(salary == 10000.0);

View File

@ -135,9 +135,7 @@ boost::asio::awaitable<void> coro_main(
// Execute the statement
boost::mysql::results result;
timer.expires_after(TIMEOUT);
op_result = co_await (
timer.async_wait() || conn.async_execute_statement(stmt, std::make_tuple(company_id), result, diag)
);
op_result = co_await (timer.async_wait() || conn.async_execute(stmt.bind(company_id), result, diag));
check_error(op_result, diag);
// Print all the obtained rows

View File

@ -64,7 +64,7 @@ void main_impl(int argc, char** argv)
// Issue the SQL query to the server
const char* sql = "SELECT 'Hello world!'";
boost::mysql::results result;
conn.query(sql, result);
conn.execute(sql, result);
//]
//[tutorial_results

View File

@ -78,7 +78,7 @@ void main_impl(int argc, char** argv)
// Execute the statement
boost::mysql::results result;
conn.execute_statement(stmt, std::make_tuple(company_id), result);
conn.execute(stmt.bind(company_id), result);
// Print employees
for (boost::mysql::row_view employee : result.rows())

View File

@ -23,6 +23,7 @@
#include <boost/mysql/string_view.hpp>
#include <boost/mysql/detail/auxiliar/access_fwd.hpp>
#include <boost/mysql/detail/auxiliar/execution_request.hpp>
#include <boost/mysql/detail/auxiliar/field_type_traits.hpp>
#include <boost/mysql/detail/auxiliar/rebind_executor.hpp>
#include <boost/mysql/detail/channel/channel.hpp>
@ -307,7 +308,152 @@ public:
);
/**
* \brief Executes a SQL text query.
* \brief Executes a text query or prepared statement.
* \details
* Sends `req` to the server for execution and reads the response into `result`.
* `req` should may be either a type convertible to \ref string_view containing valid SQL
* or a bound prepared statement, obtained by calling \ref statement::bind.
* If a string, it must be encoded using the connection's character set.
* Any string parameters provided to \ref statement::bind should also be encoded
* using the connection's character set.
* \n
* After this operation completes successfully, `result.has_value() == true`.
* \n
* Metadata in `result` will be populated according to `this->meta_mode()`.
*/
template <BOOST_MYSQL_EXECUTION_REQUEST ExecutionRequest>
void execute(const ExecutionRequest& req, results& result, error_code&, diagnostics&);
/// \copydoc execute
template <BOOST_MYSQL_EXECUTION_REQUEST ExecutionRequest>
void execute(const ExecutionRequest& req, results& result);
/**
* \copydoc execute
* \par Object lifetimes
* If `CompletionToken` is a deferred completion token (e.g. `use_awaitable`), the caller is
* responsible for managing `req`'s validity following these rules:
* \n
* \li If `req` is `string_view`, the string pointed to by `req`
* must be kept alive by the caller until the operation is initiated.
* \li If `req` is a \ref bound_statement_tuple, and any of the parameters is a reference
* type (like `string_view`), the caller must keep the values pointed by these references alive
* until the operation is initiated.
* \li If `req` is a \ref bound_statement_iterator_range, the caller must keep objects in
* the iterator range passed to \ref statement::bind alive until the operation is initiated.
*
* \par Handler signature
* The handler signature for this operation is `void(boost::mysql::error_code)`.
*/
template <
BOOST_MYSQL_EXECUTION_REQUEST ExecutionRequest,
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(
ExecutionRequest&& req,
results& result,
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
)
{
return async_execute(
std::forward<ExecutionRequest>(req),
result,
shared_diag(),
std::forward<CompletionToken>(token)
);
}
/// \copydoc async_execute
template <
BOOST_MYSQL_EXECUTION_REQUEST ExecutionRequest,
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(
ExecutionRequest&& req,
results& result,
diagnostics& diag,
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
);
/**
* \brief Starts a SQL execution as a multi-function operation.
* \details
* Writes the execution request and reads the initial server response and the column
* metadata, but not the generated rows or subsequent resultsets, if any.
* After this operation completes, `st` will have
* \ref execution_state::meta populated.
* Metadata will be populated according to `this->meta_mode()`.
* \n
* If the operation generated any rows or more than one resultset, these <b>must</b> be read (by using
* \ref read_some_rows and \ref read_resultset_head) before engaging in any further network operation.
* Otherwise, the results are undefined.
* \n
* req may be either a type convertible to \ref string_view containing valid SQL
* or a bound prepared statement, obtained by calling \ref statement::bind.
* If a string, it must be encoded using the connection's character set.
* Any string parameters provided to \ref statement::bind should also be encoded
* using the connection's character set.
*/
template <BOOST_MYSQL_EXECUTION_REQUEST ExecutionRequest>
void start_execution(const ExecutionRequest& req, execution_state& st, error_code&, diagnostics&);
/// \copydoc start_execution
template <BOOST_MYSQL_EXECUTION_REQUEST ExecutionRequest>
void start_execution(const ExecutionRequest& req, execution_state& st);
/**
* \copydoc start_execution
* \par Object lifetimes
* If `CompletionToken` is a deferred completion token (e.g. `use_awaitable`), the caller is
* responsible for managing `req`'s validity following these rules:
* \n
* \li If `req` is `string_view`, the string pointed to by `req`
* must be kept alive by the caller until the operation is initiated.
* \li If `req` is a \ref bound_statement_tuple, and any of the parameters is a reference
* type (like `string_view`), the caller must keep the values pointed by these references alive
* until the operation is initiated.
* \li If `req` is a \ref bound_statement_iterator_range, the caller must keep objects in
* the iterator range passed to \ref statement::bind alive until the operation is initiated.
*
* \par Handler signature
* The handler signature for this operation is `void(boost::mysql::error_code)`.
*/
template <
BOOST_MYSQL_EXECUTION_REQUEST ExecutionRequest,
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_execution(
ExecutionRequest&& req,
execution_state& st,
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
)
{
return async_start_execution(
std::forward<ExecutionRequest>(req),
st,
shared_diag(),
std::forward<CompletionToken>(token)
);
}
/// \copydoc async_start_execution
template <
BOOST_MYSQL_EXECUTION_REQUEST ExecutionRequest,
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_execution(
ExecutionRequest&& req,
execution_state& st,
diagnostics& diag,
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
);
/**
* \brief (Deprecated) Executes a SQL text query.
* \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.
@ -320,6 +466,10 @@ public:
* If you compose `query_string` by concatenating strings manually, <b>your code is
* vulnerable to SQL injection attacks</b>. If your query contains patameters unknown at
* compile time, use prepared statements instead of this function.
*
* \par Deprecation notice
* This function is only provided for backwards-compatibility. For new code, please
* use \ref execute or \ref async_execute instead.
*/
void query(string_view query_string, results& result, error_code&, diagnostics&);
@ -361,7 +511,7 @@ public:
);
/**
* \brief Starts a text query as a multi-function operation.
* \brief (Deprecated) 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 or subsequent resultsets, if any.
@ -374,6 +524,10 @@ public:
* Otherwise, the results are undefined.
* \n
* `query_string` should be encoded using the connection's character set.
*
* \par Deprecation notice
* This function is only provided for backwards-compatibility. For new code, please
* use \ref start_execution or \ref async_start_execution instead.
*/
void start_query(string_view query_string, execution_state& st, error_code&, diagnostics&);
@ -459,7 +613,7 @@ public:
);
/**
* \brief Executes a prepared statement.
* \brief (Deprecated) Executes a prepared statement.
* \details
* Executes a statement with the given parameters and reads the response into `result`.
* \n
@ -473,6 +627,10 @@ public:
* Metadata in `result` will be populated according to `conn.meta_mode()`, where `conn`
* is the connection that prepared this statement.
*
* \par Deprecation notice
* This function is only provided for backwards-compatibility. For new code, please
* use \ref execute or \ref async_execute instead.
*
* \par Preconditions
* `stmt.valid() == true`
*/
@ -542,7 +700,7 @@ public:
);
/**
* \brief Starts a statement execution as a multi-function operation.
* \brief (Deprecated) Starts a statement execution as a multi-function operation.
* \details
* Writes the execute request and reads the initial server response and the column
* metadata, but not the generated rows or subsequent resultsets, if any. After this operation completes,
@ -556,6 +714,10 @@ public:
* The statement actual parameters (`params`) are passed as a `std::tuple` of elements.
* String parameters should be encoded using the connection's character set.
*
* \par Deprecation notice
* This function is only provided for backwards-compatibility. For new code, please
* use \ref start_execution or \ref async_start_execution instead.
*
* \par Preconditions
* `stmt.valid() == true`
*/
@ -626,7 +788,7 @@ public:
);
/**
* \brief Starts a statement execution as a multi-function operation.
* \brief (Deprecated) Starts a statement execution as a multi-function operation.
* \details
* Writes the execute request and reads the initial server response and the column
* metadata, but not the generated rows or any subsequent resultsets, if any. After this operation
@ -639,6 +801,10 @@ public:
* The statement actual parameters are passed as an iterator range.
* String parameters should be encoded using the connection's character set.
*
* \par Deprecation notice
* This function is only provided for backwards-compatibility. For new code, please
* use \ref start_execution or \ref async_start_execution instead.
*
* \par Preconditions
* `stmt.valid() == true`
*/

View File

@ -0,0 +1,68 @@
//
// Copyright (c) 2019-2023 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_AUXILIAR_EXECUTION_REQUEST_HPP
#define BOOST_MYSQL_DETAIL_AUXILIAR_EXECUTION_REQUEST_HPP
#include <boost/mysql/statement.hpp>
#include <boost/mysql/string_view.hpp>
#include <boost/mysql/detail/config.hpp>
#include <type_traits>
namespace boost {
namespace mysql {
namespace detail {
template <class T>
struct is_bound_statement_tuple : std::false_type
{
};
template <class T>
struct is_bound_statement_tuple<bound_statement_tuple<T>> : std::true_type
{
};
template <class T>
struct is_bound_statement_range : std::false_type
{
};
template <class T>
struct is_bound_statement_range<bound_statement_iterator_range<T>> : std::true_type
{
};
template <class T>
struct is_execution_request
{
using without_cvref = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
static constexpr bool value = std::is_convertible<T, string_view>::value ||
is_bound_statement_tuple<without_cvref>::value ||
is_bound_statement_range<without_cvref>::value;
};
#ifdef BOOST_MYSQL_HAS_CONCEPTS
template <class T>
concept execution_request = is_execution_request<T>::value;
#define BOOST_MYSQL_EXECUTION_REQUEST ::boost::mysql::detail::execution_request
#else // BOOST_MYSQL_HAS_CONCEPTS
#define BOOST_MYSQL_EXECUTION_REQUEST class
#endif // BOOST_MYSQL_HAS_CONCEPTS
} // namespace detail
} // namespace mysql
} // namespace boost
#endif

View File

@ -5,8 +5,8 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_EXECUTE_HPP
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_EXECUTE_HPP
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_EXECUTE_IMPL_HPP
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_EXECUTE_IMPL_HPP
#include <boost/mysql/diagnostics.hpp>
#include <boost/mysql/error_code.hpp>
@ -25,7 +25,7 @@ namespace detail {
// before calling these
template <class Stream>
void execute(
void execute_impl(
channel<Stream>& channel,
resultset_encoding enc,
results& output,
@ -35,7 +35,7 @@ void execute(
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(
async_execute_impl(
channel<Stream>& chan,
resultset_encoding enc,
results& output,
@ -47,6 +47,6 @@ async_execute(
} // namespace mysql
} // namespace boost
#include <boost/mysql/detail/network_algorithms/impl/execute.hpp>
#include <boost/mysql/detail/network_algorithms/impl/execute_impl.hpp>
#endif

View File

@ -10,9 +10,11 @@
#include <boost/mysql/diagnostics.hpp>
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/execution_state.hpp>
#include <boost/mysql/results.hpp>
#include <boost/mysql/statement.hpp>
#include <boost/mysql/detail/auxiliar/execution_request.hpp>
#include <boost/mysql/detail/auxiliar/field_type_traits.hpp>
#include <boost/mysql/detail/channel/channel.hpp>
@ -22,43 +24,10 @@ namespace boost {
namespace mysql {
namespace detail {
template <class Stream>
void query(channel<Stream>& channel, string_view query, results& output, error_code& err, diagnostics& diag);
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,
string_view query,
results& output,
diagnostics& diag,
CompletionToken&& token
);
template <class Stream>
void start_query(
template <class Stream, BOOST_MYSQL_EXECUTION_REQUEST ExecutionRequest>
void execute(
channel<Stream>& channel,
string_view query,
execution_state& output,
error_code& err,
diagnostics& diag
);
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_start_query(
channel<Stream>& chan,
string_view query,
execution_state& output,
diagnostics& diag,
CompletionToken&& token
);
template <class Stream, BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple>
void execute_statement(
channel<Stream>& channel,
const statement& stmt,
const FieldLikeTuple& params,
const ExecutionRequest& req,
results& output,
error_code& err,
diagnostics& diag
@ -66,64 +35,35 @@ void execute_statement(
template <
class Stream,
BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple,
BOOST_MYSQL_EXECUTION_REQUEST ExecutionRequest,
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_execute_statement(
async_execute(
channel<Stream>& chan,
const statement& stmt,
FieldLikeTuple&& params,
ExecutionRequest&& req,
results& output,
diagnostics& diag,
CompletionToken&& token
);
template <class Stream, BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator>
void start_statement_execution(
template <class Stream, BOOST_MYSQL_EXECUTION_REQUEST ExecutionRequest>
void start_execution(
channel<Stream>& channel,
const statement& stmt,
FieldViewFwdIterator params_first,
FieldViewFwdIterator params_last,
execution_state& output,
const ExecutionRequest& req,
execution_state& st,
error_code& err,
diagnostics& diag
);
template <
class Stream,
BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator,
BOOST_MYSQL_EXECUTION_REQUEST ExecutionRequest,
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(
async_start_execution(
channel<Stream>& chan,
const statement& stmt,
FieldViewFwdIterator params_first,
FieldViewFwdIterator params_last,
execution_state& output,
diagnostics& diag,
CompletionToken&& token
);
template <class Stream, BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple>
void start_statement_execution(
channel<Stream>& channel,
const statement& stmt,
const FieldLikeTuple& params,
execution_state& output,
error_code& err,
diagnostics& diag
);
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& stmt,
FieldLikeTuple&& params,
execution_state& output,
ExecutionRequest&& req,
execution_state& st,
diagnostics& diag,
CompletionToken&& token
);

View File

@ -5,15 +5,14 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_EXECUTE_HPP
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_EXECUTE_HPP
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_EXECUTE_IMPL_HPP
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_EXECUTE_IMPL_HPP
#pragma once
#include <boost/mysql/detail/network_algorithms/execute.hpp>
#include <boost/mysql/detail/network_algorithms/execute_impl.hpp>
#include <boost/mysql/detail/network_algorithms/helpers.hpp>
#include <boost/mysql/detail/network_algorithms/read_resultset_head.hpp>
#include <boost/mysql/detail/network_algorithms/start_execution.hpp>
#include <boost/mysql/detail/protocol/deserialize_row.hpp>
#include <boost/mysql/detail/protocol/execution_state_impl.hpp>
@ -24,14 +23,14 @@ namespace mysql {
namespace detail {
template <class Stream>
struct execute_op : boost::asio::coroutine
struct execute_impl_op : boost::asio::coroutine
{
channel<Stream>& chan_;
resultset_encoding enc_;
execution_state_impl& st_;
diagnostics& diag_;
execute_op(
execute_impl_op(
channel<Stream>& chan,
resultset_encoding enc,
execution_state_impl& st,
@ -94,7 +93,7 @@ struct execute_op : boost::asio::coroutine
} // namespace boost
template <class Stream>
void boost::mysql::detail::execute(
void boost::mysql::detail::execute_impl(
channel<Stream>& channel,
resultset_encoding enc,
results& result,
@ -141,7 +140,7 @@ void boost::mysql::detail::execute(
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(
boost::mysql::detail::async_execute_impl(
channel<Stream>& chan,
resultset_encoding enc,
results& result,
@ -150,7 +149,7 @@ boost::mysql::detail::async_execute(
)
{
return boost::asio::async_compose<CompletionToken, void(boost::mysql::error_code)>(
execute_op<Stream>(chan, enc, results_access::get_impl(result), diag),
execute_impl_op<Stream>(chan, enc, results_access::get_impl(result), diag),
token,
chan
);

View File

@ -9,9 +9,13 @@
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_HIGH_LEVEL_EXECUTION_HPP
#pragma once
#include <boost/mysql/detail/network_algorithms/execute.hpp>
#include <boost/mysql/execution_state.hpp>
#include <boost/mysql/detail/auxiliar/execution_request.hpp>
#include <boost/mysql/detail/network_algorithms/execute_impl.hpp>
#include <boost/mysql/detail/network_algorithms/high_level_execution.hpp>
#include <boost/mysql/detail/network_algorithms/start_execution.hpp>
#include <boost/mysql/detail/network_algorithms/start_execution_impl.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>
@ -25,13 +29,33 @@ namespace boost {
namespace mysql {
namespace detail {
// Helpers
inline void serialize_query_exec_req(channel_base& chan, string_view query)
// Text queries
template <
typename T,
typename EnableIf = typename std::enable_if<std::is_convertible<T, string_view>::value>::type>
resultset_encoding get_encoding(const T&)
{
com_query_packet request{string_eof(query)};
return resultset_encoding::text;
}
template <
typename T,
typename EnableIf = typename std::enable_if<std::is_convertible<T, string_view>::value>::type>
void serialize_execution_request(const T& req, channel_base& chan)
{
com_query_packet request{string_eof(string_view(req))};
serialize_message(request, chan.current_capabilities(), chan.shared_buffer());
}
template <
typename T,
typename EnableIf = typename std::enable_if<std::is_convertible<T, string_view>::value>::type>
error_code check_client_errors(const T&)
{
return error_code();
}
// Helpers for statements
template <BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator>
void serialize_stmt_exec_req(
channel_base& chan,
@ -63,32 +87,57 @@ std::array<field_view, sizeof...(T)> tuple_to_array(const std::tuple<T...>& t) n
return tuple_to_array_impl(t, boost::mp11::make_index_sequence<sizeof...(T)>());
}
template <BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple>
void serialize_stmt_exec_req(channel_base& chan, const statement& stmt, const FieldLikeTuple& params)
{
auto arr = tuple_to_array(params);
serialize_stmt_exec_req(chan, stmt, arr.begin(), arr.end());
}
inline error_code check_num_params(const statement& stmt, std::size_t param_count)
{
return param_count == stmt.num_params() ? error_code() : make_error_code(client_errc::wrong_num_params);
}
template <BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator>
error_code check_num_params_it(
const statement& stmt,
FieldViewFwdIterator params_first,
FieldViewFwdIterator params_last
)
// Statement, tuple
template <BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple>
resultset_encoding get_encoding(const bound_statement_tuple<FieldLikeTuple>&)
{
return check_num_params(stmt, std::distance(params_first, params_last));
return resultset_encoding::binary;
}
template <BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple>
error_code check_num_params_tuple(const statement& stmt, const FieldLikeTuple&)
void serialize_execution_request(const bound_statement_tuple<FieldLikeTuple>& req, channel_base& chan)
{
return check_num_params(stmt, std::tuple_size<FieldLikeTuple>::value);
const auto& impl = statement_access::get_impl_tuple(req);
auto arr = tuple_to_array(impl.params);
serialize_stmt_exec_req(chan, impl.stmt, arr.begin(), arr.end());
}
template <BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple>
error_code check_client_errors(const bound_statement_tuple<FieldLikeTuple>& req)
{
return check_num_params(
statement_access::get_impl_tuple(req).stmt,
std::tuple_size<FieldLikeTuple>::value
);
}
// Statement, iterators
template <BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator>
resultset_encoding get_encoding(const bound_statement_iterator_range<FieldViewFwdIterator>&)
{
return resultset_encoding::binary;
}
template <BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator>
void serialize_execution_request(
const bound_statement_iterator_range<FieldViewFwdIterator>& req,
channel_base& chan
)
{
const auto& impl = statement_access::get_impl_range(req);
serialize_stmt_exec_req(chan, impl.stmt, impl.first, impl.last);
}
template <BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator>
error_code check_client_errors(const bound_statement_iterator_range<FieldViewFwdIterator>& req)
{
const auto& impl = statement_access::get_impl_range(req);
return check_num_params(impl.stmt, std::distance(impl.first, impl.last));
}
template <class Stream, class Handler>
@ -101,51 +150,18 @@ void fast_fail(channel<Stream>& chan, Handler&& handler, error_code ec)
}
// Async initiations
struct initiate_query
struct initiate_execute
{
template <class Handler, class Stream>
template <class Handler, BOOST_MYSQL_EXECUTION_REQUEST ExecutionRequest, class Stream>
void operator()(
Handler&& handler,
std::reference_wrapper<channel<Stream>> chan,
string_view query,
const ExecutionRequest& req,
results& result,
diagnostics& diag
)
{
serialize_query_exec_req(chan, query);
async_execute(chan.get(), resultset_encoding::text, result, diag, std::forward<Handler>(handler));
}
};
struct initiate_start_query
{
template <class Handler, class Stream>
void operator()(
Handler&& handler,
std::reference_wrapper<channel<Stream>> chan,
string_view query,
execution_state& st,
diagnostics& diag
)
{
serialize_query_exec_req(chan, query);
async_start_execution(chan.get(), resultset_encoding::text, st, diag, std::forward<Handler>(handler));
}
};
struct initiate_execute_statement
{
template <class Handler, class Stream, BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple>
void operator()(
Handler&& handler,
std::reference_wrapper<channel<Stream>> chan,
const statement& stmt,
const FieldLikeTuple& params,
results& result,
diagnostics& diag
)
{
auto ec = check_num_params_tuple(stmt, params);
auto ec = check_client_errors(req);
if (ec)
{
diag.clear();
@ -153,31 +169,24 @@ struct initiate_execute_statement
}
else
{
serialize_stmt_exec_req(chan, stmt, params);
async_execute(
chan.get(),
resultset_encoding::binary,
result,
diag,
std::forward<Handler>(handler)
);
serialize_execution_request(req, chan);
async_execute_impl(chan.get(), get_encoding(req), result, diag, std::forward<Handler>(handler));
}
}
};
struct initiate_start_statement_execution
struct initiate_start_execution
{
template <class Handler, class Stream, BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple>
template <class Handler, BOOST_MYSQL_EXECUTION_REQUEST ExecutionRequest, class Stream>
void operator()(
Handler&& handler,
std::reference_wrapper<channel<Stream>> chan,
const statement& stmt,
const FieldLikeTuple& params,
const ExecutionRequest& req,
execution_state& st,
diagnostics& diag
)
{
auto ec = check_num_params_tuple(stmt, params);
auto ec = check_client_errors(req);
if (ec)
{
diag.clear();
@ -185,40 +194,10 @@ struct initiate_start_statement_execution
}
else
{
serialize_stmt_exec_req(chan, stmt, params);
async_start_execution(
serialize_execution_request(req, chan);
async_start_execution_impl(
chan.get(),
resultset_encoding::binary,
st,
diag,
std::forward<Handler>(handler)
);
}
}
template <class Handler, class Stream, BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FwdIt>
void operator()(
Handler&& handler,
std::reference_wrapper<channel<Stream>> chan,
const statement& stmt,
FwdIt first,
FwdIt last,
execution_state& st,
diagnostics& diag
)
{
auto ec = check_num_params_it(stmt, first, last);
if (ec)
{
diag.clear();
fast_fail(chan.get(), std::forward<Handler>(handler), ec);
}
else
{
serialize_stmt_exec_req(chan, stmt, first, last);
async_start_execution(
chan.get(),
resultset_encoding::binary,
get_encoding(req),
st,
diag,
std::forward<Handler>(handler)
@ -231,199 +210,81 @@ struct initiate_start_statement_execution
} // namespace mysql
} // namespace boost
template <class Stream>
void boost::mysql::detail::query(
template <class Stream, BOOST_MYSQL_EXECUTION_REQUEST ExecutionRequest>
void boost::mysql::detail::execute(
channel<Stream>& channel,
string_view query,
const ExecutionRequest& req,
results& result,
error_code& err,
diagnostics& diag
)
{
serialize_query_exec_req(channel, query);
execute(channel, resultset_encoding::text, result, err, diag);
err = check_client_errors(req);
if (err)
return;
serialize_execution_request(req, channel);
execute_impl(channel, get_encoding(req), result, err, diag);
}
template <class Stream, BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
template <
class Stream,
BOOST_MYSQL_EXECUTION_REQUEST ExecutionRequest,
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(
boost::mysql::detail::async_execute(
channel<Stream>& chan,
string_view query,
ExecutionRequest&& req,
results& result,
diagnostics& diag,
CompletionToken&& token
)
{
return boost::asio::async_initiate<CompletionToken, void(boost::mysql::error_code)>(
initiate_query(),
initiate_execute(),
token,
std::ref(chan),
query,
std::forward<ExecutionRequest>(req),
std::ref(result),
std::ref(diag)
);
}
template <class Stream>
void boost::mysql::detail::start_query(
template <class Stream, BOOST_MYSQL_EXECUTION_REQUEST ExecutionRequest>
void boost::mysql::detail::start_execution(
channel<Stream>& channel,
string_view query,
const ExecutionRequest& req,
execution_state& st,
error_code& err,
diagnostics& diag
)
{
serialize_query_exec_req(channel, query);
start_execution(channel, resultset_encoding::text, st, err, diag);
}
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_start_query(
channel<Stream>& chan,
string_view query,
execution_state& st,
diagnostics& diag,
CompletionToken&& token
)
{
return boost::asio::async_initiate<CompletionToken, void(boost::mysql::error_code)>(
initiate_start_query(),
token,
std::ref(chan),
query,
std::ref(st),
std::ref(diag)
);
}
template <class Stream, BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple>
void boost::mysql::detail::execute_statement(
channel<Stream>& channel,
const statement& stmt,
const FieldLikeTuple& params,
results& result,
error_code& err,
diagnostics& diag
)
{
err = check_num_params_tuple(stmt, params);
err = check_client_errors(req);
if (err)
return;
serialize_stmt_exec_req(channel, stmt, params);
execute(channel, resultset_encoding::binary, result, err, diag);
serialize_execution_request(req, channel);
start_execution_impl(channel, get_encoding(req), st, err, diag);
}
template <
class Stream,
BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple,
BOOST_MYSQL_EXECUTION_REQUEST ExecutionRequest,
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(
boost::mysql::detail::async_start_execution(
channel<Stream>& chan,
const statement& stmt,
FieldLikeTuple&& params,
results& result,
diagnostics& diag,
CompletionToken&& token
)
{
return boost::asio::async_initiate<CompletionToken, void(boost::mysql::error_code)>(
initiate_execute_statement(),
token,
std::ref(chan),
stmt,
std::forward<FieldLikeTuple>(params),
std::ref(result),
std::ref(diag)
);
}
template <class Stream, BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple>
void boost::mysql::detail::start_statement_execution(
channel<Stream>& channel,
const statement& stmt,
const FieldLikeTuple& params,
execution_state& st,
error_code& err,
diagnostics& diag
)
{
err = check_num_params_tuple(stmt, params);
if (err)
return;
serialize_stmt_exec_req(channel, stmt, params);
start_execution(channel, resultset_encoding::binary, st, err, diag);
}
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& stmt,
FieldLikeTuple&& params,
execution_state& output,
diagnostics& diag,
CompletionToken&& token
)
{
return boost::asio::async_initiate<CompletionToken, void(boost::mysql::error_code)>(
initiate_start_statement_execution(),
token,
std::ref(chan),
stmt,
std::forward<FieldLikeTuple>(params),
std::ref(output),
std::ref(diag)
);
}
template <class Stream, BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator>
void boost::mysql::detail::start_statement_execution(
channel<Stream>& chan,
const statement& stmt,
FieldViewFwdIterator params_first,
FieldViewFwdIterator params_last,
execution_state& st,
error_code& err,
diagnostics& diag
)
{
err = check_num_params_it(stmt, params_first, params_last);
if (err)
return;
serialize_stmt_exec_req(chan, stmt, params_first, params_last);
start_execution(chan, resultset_encoding::binary, st, err, diag);
}
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& stmt,
FieldViewFwdIterator params_first,
FieldViewFwdIterator params_last,
ExecutionRequest&& req,
execution_state& st,
diagnostics& diag,
CompletionToken&& token
)
{
return boost::asio::async_initiate<CompletionToken, void(boost::mysql::error_code)>(
initiate_start_statement_execution(),
initiate_start_execution(),
token,
std::ref(chan),
stmt,
params_first,
params_last,
std::forward<ExecutionRequest>(req),
std::ref(st),
std::ref(diag)
);

View File

@ -5,13 +5,13 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_START_EXECUTION_HPP
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_START_EXECUTION_HPP
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_START_EXECUTION_IMPL_HPP
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_START_EXECUTION_IMPL_HPP
#pragma once
#include <boost/mysql/detail/network_algorithms/read_resultset_head.hpp>
#include <boost/mysql/detail/network_algorithms/start_execution.hpp>
#include <boost/mysql/detail/network_algorithms/start_execution_impl.hpp>
#include <boost/mysql/detail/protocol/execution_state_impl.hpp>
#include <boost/asio/coroutine.hpp>
@ -21,14 +21,14 @@ namespace mysql {
namespace detail {
template <class Stream>
struct start_execution_op : boost::asio::coroutine
struct start_execution_impl_op : boost::asio::coroutine
{
channel<Stream>& chan_;
resultset_encoding enc_;
execution_state_impl& st_;
diagnostics& diag_;
start_execution_op(
start_execution_impl_op(
channel<Stream>& chan,
resultset_encoding enc,
execution_state_impl& st,
@ -73,7 +73,7 @@ struct start_execution_op : boost::asio::coroutine
} // namespace boost
template <class Stream>
void boost::mysql::detail::start_execution(
void boost::mysql::detail::start_execution_impl(
channel<Stream>& channel,
resultset_encoding enc,
execution_state& st,
@ -100,7 +100,7 @@ void boost::mysql::detail::start_execution(
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_start_execution(
boost::mysql::detail::async_start_execution_impl(
channel<Stream>& channel,
resultset_encoding enc,
execution_state& st,
@ -109,7 +109,7 @@ boost::mysql::detail::async_start_execution(
)
{
return boost::asio::async_compose<CompletionToken, void(error_code)>(
start_execution_op<Stream>(channel, enc, execution_state_access::get_impl(st), diag),
start_execution_impl_op<Stream>(channel, enc, execution_state_access::get_impl(st), diag),
token,
channel
);

View File

@ -5,8 +5,8 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_START_EXECUTION_HPP
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_START_EXECUTION_HPP
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_START_EXECUTION_IMPL_HPP
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_START_EXECUTION_IMPL_HPP
#include <boost/mysql/diagnostics.hpp>
#include <boost/mysql/error_code.hpp>
@ -25,7 +25,7 @@ namespace detail {
// before calling these
template <class Stream>
void start_execution(
void start_execution_impl(
channel<Stream>& channel,
resultset_encoding encoding,
execution_state& st,
@ -35,7 +35,7 @@ void start_execution(
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_start_execution(
async_start_execution_impl(
channel<Stream>& chan,
resultset_encoding encoding,
execution_state& st,
@ -47,6 +47,6 @@ async_start_execution(
} // namespace mysql
} // namespace boost
#include <boost/mysql/detail/network_algorithms/impl/start_execution.hpp>
#include <boost/mysql/detail/network_algorithms/impl/start_execution_impl.hpp>
#endif /* INCLUDE_MYSQL_IMPL_NETWORK_ALGORITHMS_READ_RESULTSET_HEAD_HPP_ */

View File

@ -82,7 +82,7 @@ inline std::size_t boost::mysql::detail::serialization_traits<
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);
res += get_size(ctx, field_view(*it));
}
}
@ -113,7 +113,8 @@ inline void boost::mysql::detail::serialization_traits<
std::memset(ctx.first(), 0, traits.byte_count()); // Initialize to zeroes
for (auto it = input.params_begin; it != input.params_end; ++it, ++i)
{
if (it->is_null())
field_view fv(*it);
if (fv.is_null())
{
traits.set_null(ctx.first(), i);
}
@ -135,7 +136,7 @@ inline void boost::mysql::detail::serialization_traits<
// actual values
for (auto it = input.params_begin; it != input.params_end; ++it)
{
serialize(ctx, *it);
serialize(ctx, field_view(*it));
}
}
}

View File

@ -94,7 +94,7 @@ public:
/**
* \brief Returns whether `*this` is in the initial state.
* \details
* Call `start_query`, `start_statement_execution` or their async countrparts to move
* Call \ref connection::start_execution or \ref connection::async_start_execution to move
* forward. No data is available in this state.
*
* \par Exception safety

View File

@ -50,7 +50,7 @@ public:
* \param mode The \ref ssl_mode to use with this connection; ignored if
* the connection's `Stream` does not support SSL.
* \param multi_queries Whether to enable support for executing semicolon-separated
* queries using \ref connection::query. Disabled by default.
* queries using \ref connection::execute and \ref connection::start_execution. Disabled by default.
*/
handshake_params(
string_view username,

View File

@ -113,14 +113,14 @@ void boost::mysql::connection<Stream>::start_query(
)
{
detail::clear_errors(err, diag);
detail::start_query(get_channel(), query_string, result, err, diag);
detail::start_execution(get_channel(), query_string, result, err, diag);
}
template <class Stream>
void boost::mysql::connection<Stream>::start_query(string_view query_string, execution_state& result)
{
detail::error_block blk;
detail::start_query(get_channel(), query_string, result, blk.err, blk.diag);
detail::start_execution(get_channel(), query_string, result, blk.err, blk.diag);
blk.check(BOOST_CURRENT_LOCATION);
}
@ -134,7 +134,7 @@ boost::mysql::connection<Stream>::async_start_query(
CompletionToken&& token
)
{
return detail::async_start_query(
return detail::async_start_execution(
get_channel(),
query_string,
result,
@ -152,14 +152,14 @@ void boost::mysql::connection<Stream>::query(
)
{
detail::clear_errors(err, diag);
detail::query(get_channel(), query_string, result, err, diag);
detail::execute(get_channel(), query_string, result, err, diag);
}
template <class Stream>
void boost::mysql::connection<Stream>::query(string_view query_string, results& result)
{
detail::error_block blk;
detail::query(get_channel(), query_string, result, blk.err, blk.diag);
detail::execute(get_channel(), query_string, result, blk.err, blk.diag);
blk.check(BOOST_CURRENT_LOCATION);
}
@ -173,7 +173,7 @@ boost::mysql::connection<Stream>::async_query(
CompletionToken&& token
)
{
return detail::async_query(
return detail::async_execute(
get_channel(),
query_string,
result,
@ -182,6 +182,92 @@ boost::mysql::connection<Stream>::async_query(
);
}
template <class Stream>
template <BOOST_MYSQL_EXECUTION_REQUEST ExecutionRequest>
void boost::mysql::connection<Stream>::execute(
const ExecutionRequest& req,
results& result,
error_code& err,
diagnostics& diag
)
{
detail::clear_errors(err, diag);
detail::execute(get_channel(), req, result, err, diag);
}
template <class Stream>
template <BOOST_MYSQL_EXECUTION_REQUEST ExecutionRequest>
void boost::mysql::connection<Stream>::execute(const ExecutionRequest& req, results& result)
{
detail::error_block blk;
detail::execute(get_channel(), req, result, blk.err, blk.diag);
blk.check(BOOST_CURRENT_LOCATION);
}
template <class Stream>
template <
BOOST_MYSQL_EXECUTION_REQUEST ExecutionRequest,
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_execute(
ExecutionRequest&& req,
results& result,
diagnostics& diag,
CompletionToken&& token
)
{
return detail::async_execute(
get_channel(),
std::forward<ExecutionRequest>(req),
result,
diag,
std::forward<CompletionToken>(token)
);
}
template <class Stream>
template <BOOST_MYSQL_EXECUTION_REQUEST ExecutionRequest>
void boost::mysql::connection<Stream>::start_execution(
const ExecutionRequest& req,
execution_state& st,
error_code& err,
diagnostics& diag
)
{
detail::clear_errors(err, diag);
detail::start_execution(get_channel(), req, st, err, diag);
}
template <class Stream>
template <BOOST_MYSQL_EXECUTION_REQUEST ExecutionRequest>
void boost::mysql::connection<Stream>::start_execution(const ExecutionRequest& req, execution_state& st)
{
detail::error_block blk;
detail::start_execution(get_channel(), req, st, blk.err, blk.diag);
blk.check(BOOST_CURRENT_LOCATION);
}
template <class Stream>
template <
BOOST_MYSQL_EXECUTION_REQUEST ExecutionRequest,
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_execution(
ExecutionRequest&& req,
execution_state& st,
diagnostics& diag,
CompletionToken&& token
)
{
return detail::async_start_execution(
get_channel(),
std::forward<ExecutionRequest>(req),
st,
diag,
std::forward<CompletionToken>(token)
);
}
// Prepare statement
template <class Stream>
boost::mysql::statement boost::mysql::connection<Stream>::prepare_statement(
@ -228,7 +314,7 @@ void boost::mysql::connection<Stream>::execute_statement(
)
{
detail::clear_errors(err, diag);
detail::execute_statement(get_channel(), stmt, params, result, err, diag);
detail::execute(get_channel(), stmt.bind(params), result, err, diag);
}
template <class Stream>
@ -240,7 +326,7 @@ void boost::mysql::connection<Stream>::execute_statement(
)
{
detail::error_block blk;
detail::execute_statement(get_channel(), stmt, params, result, blk.err, blk.diag);
detail::execute(get_channel(), stmt.bind(params), result, blk.err, blk.diag);
blk.check(BOOST_CURRENT_LOCATION);
}
@ -258,10 +344,9 @@ boost::mysql::connection<Stream>::async_execute_statement(
CompletionToken&& token
)
{
return detail::async_execute_statement(
return detail::async_execute(
get_channel(),
stmt,
std::forward<FieldLikeTuple>(params),
stmt.bind(std::forward<FieldLikeTuple>(params)),
result,
diag,
std::forward<CompletionToken>(token)
@ -279,7 +364,7 @@ void boost::mysql::connection<Stream>::start_statement_execution(
)
{
detail::clear_errors(err, diag);
detail::start_statement_execution(get_channel(), stmt, params, result, err, diag);
detail::start_execution(get_channel(), stmt.bind(params), result, err, diag);
}
template <class Stream>
@ -291,7 +376,7 @@ void boost::mysql::connection<Stream>::start_statement_execution(
)
{
detail::error_block blk;
detail::start_statement_execution(get_channel(), stmt, params, result, blk.err, blk.diag);
detail::start_execution(get_channel(), stmt.bind(params), result, blk.err, blk.diag);
blk.check(BOOST_CURRENT_LOCATION);
}
@ -309,10 +394,9 @@ boost::mysql::connection<Stream>::async_start_statement_execution(
CompletionToken&& token
)
{
return detail::async_start_statement_execution(
return detail::async_start_execution(
get_channel(),
stmt,
std::forward<FieldLikeTuple>(params),
stmt.bind(std::forward<FieldLikeTuple>(params)),
result,
diag,
std::forward<CompletionToken>(token)
@ -332,7 +416,7 @@ void boost::mysql::connection<Stream>::start_statement_execution(
)
{
detail::clear_errors(err, diag);
detail::start_statement_execution(get_channel(), stmt, params_first, params_last, result, err, diag);
detail::start_execution(get_channel(), stmt.bind(params_first, params_last), result, err, diag);
}
template <class Stream>
@ -345,15 +429,7 @@ void boost::mysql::connection<Stream>::start_statement_execution(
)
{
detail::error_block blk;
detail::start_statement_execution(
get_channel(),
stmt,
params_first,
params_last,
result,
blk.err,
blk.diag
);
detail::start_execution(get_channel(), stmt.bind(params_first, params_last), result, blk.err, blk.diag);
blk.check(BOOST_CURRENT_LOCATION);
}
@ -371,11 +447,9 @@ boost::mysql::connection<Stream>::async_start_statement_execution(
CompletionToken&& token
)
{
return detail::async_start_statement_execution(
return detail::async_start_execution(
get_channel(),
stmt,
params_first,
params_last,
stmt.bind(params_first, params_last),
result,
diag,
std::forward<CompletionToken>(token)

View File

@ -15,6 +15,70 @@
#include <boost/mysql/detail/auxiliar/access_fwd.hpp>
#include <boost/mysql/detail/protocol/prepared_statement_messages.hpp>
template <BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple>
class boost::mysql::bound_statement_tuple
{
friend class statement;
friend struct detail::statement_access;
struct impl
{
statement stmt;
FieldLikeTuple params;
} impl_;
template <typename TupleType>
bound_statement_tuple(const statement& stmt, TupleType&& t) : impl_{stmt, std::forward<TupleType>(t)}
{
}
};
template <BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator>
class boost::mysql::bound_statement_iterator_range
{
friend class statement;
friend struct detail::statement_access;
struct impl
{
statement stmt;
FieldViewFwdIterator first;
FieldViewFwdIterator last;
} impl_;
bound_statement_iterator_range(
const statement& stmt,
FieldViewFwdIterator first,
FieldViewFwdIterator last
)
: impl_{stmt, first, last}
{
}
};
template <BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple, typename EnableIf>
boost::mysql::bound_statement_tuple<typename std::decay<FieldLikeTuple>::type> boost::mysql::statement::bind(
FieldLikeTuple&& args
) const
{
assert(valid());
return bound_statement_tuple<typename std::decay<FieldLikeTuple>::type>(
*this,
std::forward<FieldLikeTuple>(args)
);
}
template <BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator, typename EnableIf>
boost::mysql::bound_statement_iterator_range<FieldViewFwdIterator> boost::mysql::statement::bind(
FieldViewFwdIterator first,
FieldViewFwdIterator last
) const
{
assert(valid());
return bound_statement_iterator_range<FieldViewFwdIterator>(*this, first, last);
}
struct boost::mysql::detail::statement_access
{
static void reset(statement& stmt, const detail::com_stmt_prepare_ok_packet& msg) noexcept
@ -23,6 +87,22 @@ struct boost::mysql::detail::statement_access
stmt.id_ = msg.statement_id;
stmt.num_params_ = msg.num_params;
}
template <BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple>
static const typename bound_statement_tuple<FieldLikeTuple>::impl get_impl_tuple(
const bound_statement_tuple<FieldLikeTuple>& obj
)
{
return obj.impl_;
}
template <BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator>
static const typename bound_statement_iterator_range<FieldViewFwdIterator>::impl get_impl_range(
const bound_statement_iterator_range<FieldViewFwdIterator>& obj
)
{
return obj.impl_;
}
};
#endif

View File

@ -123,8 +123,8 @@ public:
/**
* \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 connection::execute_statement or their async
* counterparts are guaranteed to have `this->has_value() == true`.
* Objects populated by \ref connection::execute and \ref connection::async_execute
* are guaranteed to have `this->has_value() == true`.
*
* \par Exception safety
* No-throw guarantee.

View File

@ -9,13 +9,34 @@
#define BOOST_MYSQL_STATEMENT_HPP
#include <boost/mysql/detail/auxiliar/access_fwd.hpp>
#include <boost/mysql/detail/auxiliar/field_type_traits.hpp>
#include <cassert>
#include <cstdint>
#include <tuple>
#include <type_traits>
namespace boost {
namespace mysql {
/**
* \brief A statement with bound parameters, represented as a `std::tuple`.
* \details
* This class satisfies `ExecutionRequest`. You can pass instances of this class to \ref connection::execute,
* \ref connection::start_execution or their async counterparts.
*/
template <BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple>
class bound_statement_tuple;
/**
* \brief A statement with bound parameters, represented as an iterator range.
* \details
* This class satisfies `ExecutionRequest`. You can pass instances of this class to \ref connection::execute,
* \ref connection::start_execution or their async counterparts.
*/
template <BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator>
class bound_statement_iterator_range;
/**
* \brief Represents a server-side prepared statement.
* \details
@ -82,6 +103,84 @@ public:
return num_params_;
}
/**
* \brief Binds parameters to a statement.
* \details
* Creates an object that packages `*this` and the statement actual parameters `params`.
* This object can be passed to \ref connection::execute, \ref connection::start_execution
* and their async counterparts.
* \n
* The parameters are copied into a `std::tuple` by using `std::make_tuple`. This function
* only participates in overload resolution if `std::make_tuple(FWD(args)...)` yields a
* `FieldLikeTuple`. Equivalent to `this->bind(std::make_tuple(std::forward<T>(params)...))`.
* \n
* This function doesn't involve communication with the server.
*
* \par Preconditions
* `this->valid() == true`
* \n
* \par Exception safety
* Strong guarantee. Only throws if constructing any of the internal tuple elements throws.
*/
template <class... T>
#ifdef BOOST_MYSQL_DOXYGEN
bound_statement_tuple<std::tuple<__see_below__>>
#else
auto
#endif
bind(T&&... params) const->typename std::enable_if<
detail::is_field_like_tuple<decltype(std::make_tuple(std::forward<T>(params)...))>::value,
bound_statement_tuple<decltype(std::make_tuple(std::forward<T>(params)...))>>::type
{
return bind(std::make_tuple(std::forward<T>(params)...));
}
/**
* \brief Binds parameters to a statement.
* \details
* Creates an object that packages `*this` and the statement actual parameters `params`.
* This object can be passed to \ref connection::execute, \ref connection::start_execution
* or their async counterparts.
* \n
* The `params` tuple is decay-copied into the returned object.
* \n
* This function doesn't involve communication with the server.
*
* \par Preconditions
* `this->valid() == true`
* \n
* \par Exception safety
* Strong guarantee. Only throws if the decay-copy of the tuple throws.
*/
template <
BOOST_MYSQL_FIELD_LIKE_TUPLE FieldLikeTuple,
typename EnableIf = detail::enable_if_field_like_tuple<FieldLikeTuple>>
bound_statement_tuple<typename std::decay<FieldLikeTuple>::type> bind(FieldLikeTuple&& params) const;
/**
* \brief Binds parameters to a statement (iterator range overload).
* \details
* Creates an object that packages `*this` and the statement actual parameters, represented
* as the iterator range `[params_first, params_last)`.
* This object can be passed to \ref connection::execute, \ref connection::start_execution
* or their async counterparts.
* \n
* This function doesn't involve communication with the server.
*
* \par Preconditions
* `this->valid() == true`
* \n
* \par Exception safety
* Strong guarantee. Only throws if copy-constructing iterators throws.
*/
template <
BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR FieldViewFwdIterator,
typename EnableIf = detail::enable_if_field_view_forward_iterator<FieldViewFwdIterator>>
bound_statement_iterator_range<FieldViewFwdIterator> bind(
FieldViewFwdIterator params_first,
FieldViewFwdIterator params_last
) const;
private:
bool valid_{false};
std::uint32_t id_{0};

View File

@ -31,7 +31,7 @@ BOOST_FIXTURE_TEST_CASE(query_empty_select, tcp_network_fixture)
// Issue query
results result;
conn.query("SELECT * FROM empty_table", result);
conn.execute("SELECT * FROM empty_table", result);
// Verify results
BOOST_TEST(result.size() == 1u);
@ -49,7 +49,7 @@ BOOST_FIXTURE_TEST_CASE(query_empty_select_multifn, tcp_network_fixture)
// Issue query
boost::mysql::execution_state st;
conn.start_query("SELECT * FROM empty_table", st);
conn.start_execution("SELECT * FROM empty_table", st);
BOOST_TEST_REQUIRE(st.should_read_rows());
validate_2fields_meta(st.meta(), "empty_table");
@ -69,7 +69,7 @@ BOOST_FIXTURE_TEST_CASE(query_insert, tcp_network_fixture)
// Issue query
results result;
conn.query("INSERT INTO inserts_table (field_varchar, field_date) VALUES ('v0', '2010-10-11')", result);
conn.execute("INSERT INTO inserts_table (field_varchar, field_date) VALUES ('v0', '2010-10-11')", result);
// Verify results
BOOST_TEST(result.size() == 1u);
@ -81,7 +81,7 @@ BOOST_FIXTURE_TEST_CASE(query_insert, tcp_network_fixture)
BOOST_TEST(result.info() == "");
// Verify insertion took place
conn.query("SELECT COUNT(*) FROM inserts_table", result);
conn.execute("SELECT COUNT(*) FROM inserts_table", result);
BOOST_TEST(result.rows().at(0).at(0).as_int64() == 1);
}
@ -92,7 +92,7 @@ BOOST_FIXTURE_TEST_CASE(query_update, tcp_network_fixture)
// Issue the query
results result;
conn.query("UPDATE updates_table SET field_int = field_int+10", result);
conn.execute("UPDATE updates_table SET field_int = field_int+10", result);
// Validate results
BOOST_TEST(result.size() == 1u);
@ -104,7 +104,7 @@ BOOST_FIXTURE_TEST_CASE(query_update, tcp_network_fixture)
BOOST_TEST(result.info() == "Rows matched: 3 Changed: 2 Warnings: 0");
// Validate it took effect
conn.query("SELECT field_int FROM updates_table WHERE field_varchar = 'f0'", result);
conn.execute("SELECT field_int FROM updates_table WHERE field_varchar = 'f0'", result);
BOOST_TEST(result.rows().at(0).at(0).as_int64() == 52); // initial value was 42
}
@ -115,7 +115,7 @@ BOOST_FIXTURE_TEST_CASE(query_delete, tcp_network_fixture)
// Issue the query
results result;
conn.query("DELETE FROM updates_table", result);
conn.execute("DELETE FROM updates_table", result);
// Validate results
BOOST_TEST(result.size() == 1u);
@ -127,7 +127,7 @@ BOOST_FIXTURE_TEST_CASE(query_delete, tcp_network_fixture)
BOOST_TEST(result.info() == "");
// Validate it took effect
conn.query("SELECT COUNT(*) FROM updates_table", result);
conn.execute("SELECT COUNT(*) FROM updates_table", result);
BOOST_TEST(result.rows().at(0).at(0).as_int64() == 0);
}
@ -142,7 +142,7 @@ BOOST_FIXTURE_TEST_CASE(statement_update, tcp_network_fixture)
// Execute it
results result;
conn.execute_statement(stmt, std::make_tuple(200, "f0"), result);
conn.execute(stmt.bind(200, "f0"), result);
BOOST_TEST(result.size() == 1u);
BOOST_TEST(result.meta().empty());
BOOST_TEST(result.rows().empty());
@ -152,7 +152,7 @@ BOOST_FIXTURE_TEST_CASE(statement_update, tcp_network_fixture)
BOOST_TEST(result.info() == "Rows matched: 1 Changed: 1 Warnings: 0");
// Verify that it took effect
conn.query("SELECT field_int FROM updates_table WHERE field_varchar = 'f0'", result);
conn.execute("SELECT field_int FROM updates_table WHERE field_varchar = 'f0'", result);
BOOST_TEST(result.rows().at(0).at(0).as_int64() == 200);
// Close the statement
@ -170,7 +170,7 @@ BOOST_FIXTURE_TEST_CASE(statement_delete, tcp_network_fixture)
// Execute it
results result;
conn.execute_statement(stmt, std::make_tuple("f0"), result);
conn.execute(stmt.bind("f0"), result);
BOOST_TEST(result.size() == 1u);
BOOST_TEST(result.meta().empty());
BOOST_TEST(result.rows().empty());
@ -180,7 +180,7 @@ BOOST_FIXTURE_TEST_CASE(statement_delete, tcp_network_fixture)
BOOST_TEST(result.info() == "");
// Validate it took effect
conn.query("SELECT COUNT(*) FROM updates_table", result);
conn.execute("SELECT COUNT(*) FROM updates_table", result);
BOOST_TEST(result.rows().at(0).at(0).as_int64() == 2);
}

View File

@ -65,13 +65,13 @@ struct database_types_fixture : tcp_network_fixture
void set_time_zone()
{
results result;
conn.query("SET session time_zone = '+02:00'", result);
conn.execute("SET session time_zone = '+02:00'", result);
}
void set_sql_mode()
{
results result;
conn.query("SET session sql_mode = 'ALLOW_INVALID_DATES'", result);
conn.execute("SET session sql_mode = 'ALLOW_INVALID_DATES'", result);
}
database_types_fixture()
@ -663,7 +663,7 @@ BOOST_FIXTURE_TEST_CASE(query_read, database_types_fixture)
{
// Execute the query
results result;
conn.query(table.select_sql(), result);
conn.execute(table.select_sql(), result);
// Validate the received contents
validate_meta(result.meta(), table.metas);
@ -683,7 +683,7 @@ BOOST_FIXTURE_TEST_CASE(statement_read, database_types_fixture)
// Execute it with the provided parameters
results result;
conn.execute_statement(stmt, std::make_tuple(), result);
conn.execute(stmt.bind(), result);
// Validate the received contents
validate_meta(result.meta(), table.metas);
@ -706,18 +706,18 @@ BOOST_FIXTURE_TEST_CASE(statement_write, database_types_fixture)
// Remove all contents from the table
results result;
conn.query(table.delete_sql(), result);
conn.execute(table.delete_sql(), result);
// Insert all the contents again
boost::mysql::execution_state st;
for (const auto& row : table.rws)
{
conn.start_statement_execution(insert_stmt, row.begin(), row.end(), st);
conn.start_execution(insert_stmt.bind(row.begin(), row.end()), st);
BOOST_TEST_REQUIRE(st.complete());
}
// Query them again and verify the insertion was okay
conn.execute_statement(query_stmt, std::make_tuple(), result);
conn.execute(query_stmt.bind(), result);
validate_meta(result.meta(), table.metas);
table.validate_rows(result.rows());
}

View File

@ -33,7 +33,7 @@ BOOST_FIXTURE_TEST_CASE(mysql_specific_error_code, tcp_network_fixture)
results result;
// This is reported as a common, less desriptive error in MySQL5 and MariaDB
conn.query("select * from one_row_table where field_varchar regexp '(('", result, ec, diag);
conn.execute("select * from one_row_table where field_varchar regexp '(('", result, ec, diag);
error_code expected_ec(
boost::mysql::mysql_server_errc::er_regexp_mismatched_paren,
boost::mysql::get_mysql_server_category()
@ -51,7 +51,7 @@ BOOST_FIXTURE_TEST_CASE(mariadb_specific_error_code, tcp_network_fixture)
results result;
// This is reported as a common error in MySQL5 and MySQL8
conn.query("WITH abc AS (SELECT 1), abc as (SELECT 2) SELECT * FROM abc", result, ec, diag);
conn.execute("WITH abc AS (SELECT 1), abc as (SELECT 2) SELECT * FROM abc", result, ec, diag);
error_code expected_ec(
boost::mysql::mariadb_server_errc::er_dup_query_name,
boost::mysql::get_mariadb_server_category()

View File

@ -126,7 +126,7 @@ struct caching_sha2_fixture : handshake_fixture
tcp_ssl_connection conn(ctx, ssl_ctx);
boost::mysql::results result;
conn.connect(get_endpoint<tcp_socket>(), handshake_params("root", ""));
conn.query("FLUSH PRIVILEGES", result);
conn.execute("FLUSH PRIVILEGES", result);
conn.close();
}
};

View File

@ -31,7 +31,7 @@ BOOST_FIXTURE_TEST_CASE(empty_results, tcp_network_fixture)
start_transaction();
results result;
conn.query(
conn.execute(
R"%(
INSERT INTO inserts_table (field_varchar) VALUES ('abc');
INSERT INTO inserts_table (field_varchar) VALUES ('def');
@ -75,7 +75,7 @@ BOOST_FIXTURE_TEST_CASE(data_results, tcp_network_fixture)
start_transaction();
results result;
conn.query(
conn.execute(
R"%(
SELECT * FROM one_row_table;
SELECT * FROM empty_table;
@ -120,7 +120,7 @@ BOOST_FIXTURE_TEST_CASE(error_not_enabled, tcp_network_fixture)
error_code err;
diagnostics diag;
conn.query("SELECT 1; SELECT 2", result, err, diag);
conn.execute("SELECT 1; SELECT 2", result, err, diag);
BOOST_TEST(err == common_server_errc::er_parse_error);
validate_string_contains(diag.server_message(), {"you have an error in your sql syntax"});
}

View File

@ -41,13 +41,13 @@ BOOST_FIXTURE_TEST_CASE(multiple_executions, tcp_network_fixture)
// Execute it. Only one row will be returned (because of the id)
results result;
conn.execute_statement(stmt, std::make_tuple(1, "non_existent"), result);
conn.execute(stmt.bind(1, "non_existent"), result);
validate_2fields_meta(result.meta(), "two_rows_table");
BOOST_TEST_REQUIRE(result.rows().size() == 1u);
BOOST_TEST((result.rows()[0] == makerow(1, "f0")));
// Execute it again, but with different values. This time, two rows are returned
conn.execute_statement(stmt, std::make_tuple(1, "f1"), result);
conn.execute(stmt.bind(1, "f1"), result);
validate_2fields_meta(result.meta(), "two_rows_table");
BOOST_TEST_REQUIRE(result.rows().size() == 2u);
BOOST_TEST((result.rows()[0] == makerow(1, "f0")));
@ -72,17 +72,17 @@ BOOST_FIXTURE_TEST_CASE(multiple_statements, tcp_network_fixture)
BOOST_TEST(stmt_update.id() != stmt_select.id());
// Execute update
conn.execute_statement(stmt_update, std::make_tuple(210, "f0"), result);
conn.execute(stmt_update.bind(210, "f0"), result);
BOOST_TEST(result.meta().size() == 0u);
BOOST_TEST(result.affected_rows() == 1u);
// Execute select
conn.execute_statement(stmt_select, std::make_tuple("f0"), result);
conn.execute(stmt_select.bind("f0"), result);
BOOST_TEST(result.rows().size() == 1u);
BOOST_TEST(result.rows()[0] == makerow(210));
// Execute update again
conn.execute_statement(stmt_update, std::make_tuple(220, "f0"), result);
conn.execute(stmt_update.bind(220, "f0"), result);
BOOST_TEST(result.meta().size() == 0u);
BOOST_TEST(result.affected_rows() == 1u);
@ -90,7 +90,7 @@ BOOST_FIXTURE_TEST_CASE(multiple_statements, tcp_network_fixture)
conn.close_statement(stmt_update);
// Execute select again
conn.execute_statement(stmt_select, std::make_tuple("f0"), result);
conn.execute(stmt_select.bind("f0"), result);
BOOST_TEST(result.rows().size() == 1u);
BOOST_TEST(result.rows()[0] == makerow(220));
@ -109,7 +109,7 @@ BOOST_FIXTURE_TEST_CASE(statement_without_params, tcp_network_fixture)
// Execute doesn't error
results result;
conn.execute_statement(stmt, std::make_tuple(), result);
conn.execute(stmt.bind(), result);
validate_2fields_meta(result.meta(), "empty_table");
BOOST_TEST(result.rows().size() == 0u);
}
@ -124,7 +124,7 @@ BOOST_FIXTURE_TEST_CASE(multifn, tcp_network_fixture)
// Execute it
execution_state st;
conn.start_statement_execution(stmt, std::make_tuple(), st);
conn.start_execution(stmt.bind(), st);
BOOST_TEST_REQUIRE(st.should_read_rows());
// We don't know how many rows there will be in each batch,

View File

@ -33,7 +33,7 @@ struct reconnect_fixture : network_fixture
void do_query_ok()
{
results result;
conn->query("SELECT * FROM empty_table", result).get();
conn->execute("SELECT * FROM empty_table", result).get();
BOOST_TEST(result.rows().empty());
}
};

View File

@ -65,8 +65,8 @@ BOOST_MYSQL_NETWORK_TEST(connect_error, network_fixture, err_net_samples)
BOOST_TEST(!conn->is_open());
}
// Start query
BOOST_MYSQL_NETWORK_TEST(start_query_success, network_fixture, all_network_samples())
// Start query (legacy)
BOOST_MYSQL_NETWORK_TEST(start_query_legacy_success, network_fixture, all_network_samples())
{
setup_and_connect(sample.net);
@ -76,7 +76,7 @@ BOOST_MYSQL_NETWORK_TEST(start_query_success, network_fixture, all_network_sampl
validate_2fields_meta(st.meta(), "empty_table");
}
BOOST_MYSQL_NETWORK_TEST(start_query_error, network_fixture, err_net_samples)
BOOST_MYSQL_NETWORK_TEST(start_query_legacy_error, network_fixture, err_net_samples)
{
setup_and_connect(sample.net);
@ -85,8 +85,28 @@ BOOST_MYSQL_NETWORK_TEST(start_query_error, network_fixture, err_net_samples)
.validate_error(common_server_errc::er_bad_field_error, {"unknown column", "field_bad"});
}
// Query
BOOST_MYSQL_NETWORK_TEST(query_success, network_fixture, all_network_samples())
// Start execution (query)
BOOST_MYSQL_NETWORK_TEST(start_execution_query_success, network_fixture, all_network_samples())
{
setup_and_connect(sample.net);
execution_state st;
conn->start_execution("SELECT * FROM empty_table", st).get();
BOOST_TEST(st.should_read_rows());
validate_2fields_meta(st.meta(), "empty_table");
}
BOOST_MYSQL_NETWORK_TEST(start_execution_query_error, network_fixture, err_net_samples)
{
setup_and_connect(sample.net);
execution_state st;
conn->start_execution("SELECT field_varchar, field_bad FROM one_row_table", st)
.validate_error(common_server_errc::er_bad_field_error, {"unknown column", "field_bad"});
}
// Query (legacy)
BOOST_MYSQL_NETWORK_TEST(query_legacy_success, network_fixture, all_network_samples())
{
setup_and_connect(sample.net);
@ -97,7 +117,7 @@ BOOST_MYSQL_NETWORK_TEST(query_success, network_fixture, all_network_samples())
BOOST_TEST(result.meta().size() == 2u);
}
BOOST_MYSQL_NETWORK_TEST(query_error, network_fixture, err_net_samples)
BOOST_MYSQL_NETWORK_TEST(query_legacy_error, network_fixture, err_net_samples)
{
setup_and_connect(sample.net);
@ -106,6 +126,27 @@ BOOST_MYSQL_NETWORK_TEST(query_error, network_fixture, err_net_samples)
.validate_error(common_server_errc::er_bad_field_error, {"unknown column", "field_bad"});
}
// execute (query)
BOOST_MYSQL_NETWORK_TEST(execute_query_success, network_fixture, all_network_samples())
{
setup_and_connect(sample.net);
results result;
conn->execute("SELECT 'hello', 42", result).get();
BOOST_TEST(result.rows().size() == 1u);
BOOST_TEST(result.rows()[0] == makerow("hello", 42));
BOOST_TEST(result.meta().size() == 2u);
}
BOOST_MYSQL_NETWORK_TEST(execute_query_error, network_fixture, err_net_samples)
{
setup_and_connect(sample.net);
results result;
conn->execute("SELECT field_varchar, field_bad FROM one_row_table", result)
.validate_error(common_server_errc::er_bad_field_error, {"unknown column", "field_bad"});
}
// Prepare statement
BOOST_MYSQL_NETWORK_TEST(prepare_statement_success, network_fixture, all_network_samples())
{
@ -123,8 +164,8 @@ BOOST_MYSQL_NETWORK_TEST(prepare_statement_error, network_fixture, err_net_sampl
.validate_error(common_server_errc::er_no_such_table, {"table", "doesn't exist", "bad_table"});
}
// Start statement execution (iterator version)
BOOST_MYSQL_NETWORK_TEST(start_statement_execution_it_success, network_fixture, all_network_samples())
// Start statement execution (legacy, iterator)
BOOST_MYSQL_NETWORK_TEST(start_statement_execution_legacy_it_success, network_fixture, all_network_samples())
{
setup_and_connect(sample.net);
@ -139,7 +180,7 @@ BOOST_MYSQL_NETWORK_TEST(start_statement_execution_it_success, network_fixture,
BOOST_TEST(st.should_read_rows());
}
BOOST_MYSQL_NETWORK_TEST(start_statement_execution_it_error, network_fixture, err_net_samples)
BOOST_MYSQL_NETWORK_TEST(start_statement_execution_legacy_it_error, network_fixture, err_net_samples)
{
setup_and_connect(sample.net);
start_transaction();
@ -158,8 +199,28 @@ BOOST_MYSQL_NETWORK_TEST(start_statement_execution_it_error, network_fixture, er
);
}
// Start statement execution (tuple version)
BOOST_MYSQL_NETWORK_TEST(start_statement_execution_tuple_success, network_fixture, all_network_samples())
// Start execution (statement, iterator). No error spotcheck, since it's the same underlying function
BOOST_MYSQL_NETWORK_TEST(start_execution_stmt_it_success, network_fixture, all_network_samples())
{
setup_and_connect(sample.net);
// Prepare
auto stmt = conn->prepare_statement("SELECT * FROM empty_table WHERE id IN (?, ?)").get();
// Execute
execution_state st;
std::forward_list<field_view> params{field_view("item"), field_view(42)};
conn->start_execution(stmt.bind(params.cbegin(), params.cend()), st).validate_no_error();
validate_2fields_meta(st.meta(), "empty_table");
BOOST_TEST(st.should_read_rows());
}
// Start statement execution (legacy, tuple)
BOOST_MYSQL_NETWORK_TEST(
start_statement_execution_legacy_tuple_success,
network_fixture,
all_network_samples()
)
{
setup_and_connect(sample.net);
@ -173,7 +234,7 @@ BOOST_MYSQL_NETWORK_TEST(start_statement_execution_tuple_success, network_fixtur
BOOST_TEST(st.should_read_rows());
}
BOOST_MYSQL_NETWORK_TEST(start_statement_execution_tuple_error, network_fixture, err_net_samples)
BOOST_MYSQL_NETWORK_TEST(start_statement_execution_legacy_tuple_error, network_fixture, err_net_samples)
{
setup_and_connect(sample.net);
start_transaction();
@ -191,8 +252,23 @@ BOOST_MYSQL_NETWORK_TEST(start_statement_execution_tuple_error, network_fixture,
);
}
// Execute statement
BOOST_MYSQL_NETWORK_TEST(execute_statement_success, network_fixture, all_network_samples())
// start execution (statement, tuple). No error spotcheck since it's the same underlying fn
BOOST_MYSQL_NETWORK_TEST(start_execution_statement_tuple_success, network_fixture, all_network_samples())
{
setup_and_connect(sample.net);
// Prepare
auto stmt = conn->prepare_statement("SELECT * FROM empty_table WHERE id IN (?, ?)").get();
// Execute
execution_state st;
conn->start_execution(stmt.bind(field_view(42), field_view(40)), st).validate_no_error();
validate_2fields_meta(st.meta(), "empty_table");
BOOST_TEST(st.should_read_rows());
}
// Execute statement (legacy)
BOOST_MYSQL_NETWORK_TEST(execute_statement_legacy_success, network_fixture, all_network_samples())
{
setup_and_connect(sample.net);
@ -205,7 +281,7 @@ BOOST_MYSQL_NETWORK_TEST(execute_statement_success, network_fixture, all_network
BOOST_TEST(result.rows().size() == 0u);
}
BOOST_MYSQL_NETWORK_TEST(execute_statement_error, network_fixture, err_net_samples)
BOOST_MYSQL_NETWORK_TEST(execute_statement_legacy_error, network_fixture, err_net_samples)
{
setup_and_connect(sample.net);
start_transaction();
@ -223,6 +299,35 @@ BOOST_MYSQL_NETWORK_TEST(execute_statement_error, network_fixture, err_net_sampl
);
}
// Execute (statement, iterator). No error spotcheck since it's the same underlying fn
BOOST_MYSQL_NETWORK_TEST(execute_statement_iterator_success, network_fixture, err_net_samples)
{
setup_and_connect(sample.net);
// Prepare
auto stmt = conn->prepare_statement("SELECT * FROM empty_table WHERE id IN (?, ?)").get();
// Execute
results result;
std::forward_list<field_view> params{field_view("item"), field_view(42)};
conn->execute(stmt.bind(params.cbegin(), params.cend()), result).validate_no_error();
BOOST_TEST(result.rows().size() == 0u);
}
// Execute (statement, tuple). No error spotcheck since it's the same underlying fn
BOOST_MYSQL_NETWORK_TEST(execute_statement_tuple_success, network_fixture, err_net_samples)
{
setup_and_connect(sample.net);
// Prepare
auto stmt = conn->prepare_statement("SELECT * FROM empty_table WHERE id IN (?, ?)").get();
// Execute
results result;
conn->execute(stmt.bind(field_view("item"), field_view(42)), result).validate_no_error();
BOOST_TEST(result.rows().size() == 0u);
}
// Close statement: no server error spotcheck
BOOST_MYSQL_NETWORK_TEST(close_statement_success, network_fixture, all_network_samples())
{

View File

@ -36,7 +36,7 @@ BOOST_FIXTURE_TEST_CASE(without_selects, tcp_network_fixture)
// Call the procedure
results result;
conn.execute_statement(stmt, std::make_tuple("abc"), result);
conn.execute(stmt.bind("abc"), result);
// Verify results
BOOST_TEST_REQUIRE(result.size() == 1u);
@ -49,7 +49,7 @@ BOOST_FIXTURE_TEST_CASE(without_selects, tcp_network_fixture)
BOOST_TEST(result.out_params() == row_view());
// Verify it took place
conn.query("SELECT field_varchar FROM inserts_table", result);
conn.execute("SELECT field_varchar FROM inserts_table", result);
BOOST_TEST(result.rows().at(0).at(0).as_string() == "abc");
}
@ -62,7 +62,7 @@ BOOST_FIXTURE_TEST_CASE(with_one_select, tcp_network_fixture)
// Call the procedure
results result;
conn.execute_statement(stmt, std::make_tuple("abc"), result);
conn.execute(stmt.bind("abc"), result);
// Verify results
BOOST_TEST_REQUIRE(result.size() == 2u);
@ -90,7 +90,7 @@ BOOST_FIXTURE_TEST_CASE(with_two_selects, tcp_network_fixture)
// Call the procedure
results result;
conn.execute_statement(stmt, std::make_tuple("abc", 42), result);
conn.execute(stmt.bind("abc", 42), result);
// Verify results
BOOST_TEST_REQUIRE(result.size() == 3u);
@ -124,7 +124,7 @@ BOOST_FIXTURE_TEST_CASE(with_two_selects_multifn, tcp_network_fixture)
// Call the procedure
execution_state st;
conn.start_statement_execution(stmt, std::make_tuple("abc", 42), st);
conn.start_execution(stmt.bind("abc", 42), st);
BOOST_TEST_REQUIRE(st.should_read_rows());
validate_2fields_meta(st.meta(), "one_row_table");
@ -173,7 +173,7 @@ BOOST_FIXTURE_TEST_CASE(output_params_not_bound, tcp_network_fixture)
// Call the procedure
results result;
conn.execute_statement(stmt, std::make_tuple(10), result);
conn.execute(stmt.bind(10), result);
// Verify results
BOOST_TEST_REQUIRE(result.size() == 2u);
@ -197,7 +197,7 @@ BOOST_FIXTURE_TEST_CASE(output_params_bound, tcp_network_fixture)
// Call the procedure
results result;
conn.execute_statement(stmt, std::make_tuple(10, nullptr, 30), result);
conn.execute(stmt.bind(10, nullptr, 30), result);
// Verify results
BOOST_TEST_REQUIRE(result.size() == 3u);
@ -230,7 +230,7 @@ BOOST_FIXTURE_TEST_CASE(output_params_bound_multifn, tcp_network_fixture)
// Call the procedure
execution_state st;
conn.start_statement_execution(stmt, std::make_tuple(10, nullptr, 30), st);
conn.start_execution(stmt.bind(10, nullptr, 30), st);
BOOST_TEST_REQUIRE(st.should_read_rows());
validate_2fields_meta(st.meta(), "one_row_table");
@ -277,7 +277,7 @@ BOOST_FIXTURE_TEST_CASE(with_signal, tcp_network_fixture)
results result;
error_code err;
diagnostics diag;
conn.execute_statement(stmt, std::make_tuple(), result, err, diag);
conn.execute(stmt.bind(), result, err, diag);
// Verify results
BOOST_TEST(err == common_server_errc::er_no);
@ -290,7 +290,7 @@ BOOST_FIXTURE_TEST_CASE(with_query, tcp_network_fixture)
// Call the procedure
results result;
conn.query("CALL sp_outparams(42, @var1, @var2)", result);
conn.execute("CALL sp_outparams(42, @var1, @var2)", result);
// Verify results
BOOST_TEST_REQUIRE(result.size() == 2u);

View File

@ -64,6 +64,12 @@ public:
fv_list_it params_last,
execution_state& st
) = 0;
virtual network_result<void> execute(string_view, results&) = 0;
virtual network_result<void> execute(bound_statement_tuple<std::tuple<field_view, field_view>>, results&) = 0;
virtual network_result<void> execute(bound_statement_iterator_range<fv_list_it>, results&) = 0;
virtual network_result<void> start_execution(string_view, execution_state&) = 0;
virtual network_result<void> start_execution(bound_statement_tuple<std::tuple<field_view, field_view>>, execution_state&) = 0;
virtual network_result<void> start_execution(bound_statement_iterator_range<fv_list_it>, execution_state&) = 0;
virtual network_result<void> close_statement(statement&) = 0;
virtual network_result<void> read_resultset_head(execution_state& st) = 0;
virtual network_result<rows_view> read_some_rows(execution_state& st) = 0;

View File

@ -105,7 +105,7 @@ struct network_fixture : network_fixture_base
void start_transaction()
{
results result;
conn->query("START TRANSACTION", result).get();
conn->execute("START TRANSACTION", result).get();
}
};

View File

@ -31,7 +31,7 @@ struct tcp_network_fixture : network_fixture_base
void start_transaction()
{
results result;
conn.query("START TRANSACTION", result);
conn.execute("START TRANSACTION", result);
}
};

View File

@ -123,6 +123,12 @@ function_table<stream_type> create_table()
BOOST_MYSQL_ASYNC_COROCPP20_TABLE_ENTRY(async_execute_statement),
BOOST_MYSQL_ASYNC_COROCPP20_TABLE_ENTRY(async_start_statement_execution),
BOOST_MYSQL_ASYNC_COROCPP20_TABLE_ENTRY(async_start_statement_execution),
BOOST_MYSQL_ASYNC_COROCPP20_TABLE_ENTRY(async_execute),
BOOST_MYSQL_ASYNC_COROCPP20_TABLE_ENTRY(async_execute),
BOOST_MYSQL_ASYNC_COROCPP20_TABLE_ENTRY(async_execute),
BOOST_MYSQL_ASYNC_COROCPP20_TABLE_ENTRY(async_start_execution),
BOOST_MYSQL_ASYNC_COROCPP20_TABLE_ENTRY(async_start_execution),
BOOST_MYSQL_ASYNC_COROCPP20_TABLE_ENTRY(async_start_execution),
BOOST_MYSQL_ASYNC_COROCPP20_TABLE_ENTRY(async_close_statement),
BOOST_MYSQL_ASYNC_COROCPP20_TABLE_ENTRY(async_read_resultset_head),
BOOST_MYSQL_ASYNC_COROCPP20_TABLE_ENTRY(async_read_some_rows),

View File

@ -45,19 +45,29 @@ template <class Stream>
struct function_table
{
using conn_type = connection<Stream>;
using stmt_tuple = bound_statement_tuple<std::tuple<field_view, field_view>>;
using stmt_it = bound_statement_iterator_range<fv_list_it>;
using connect_sig = network_result<
void>(conn_type&, const typename Stream::lowest_layer_type::endpoint_type&, const handshake_params&);
using handshake_sig = network_result<void>(conn_type&, const handshake_params&);
using query_sig = network_result<void>(conn_type&, string_view, results&);
using start_query_sig = network_result<void>(conn_type&, string_view, execution_state&);
using query_legacy_sig = network_result<void>(conn_type&, string_view, results&);
using start_query_legacy_sig = network_result<void>(conn_type&, string_view, execution_state&);
using prepare_statement_sig = network_result<statement>(conn_type&, string_view);
using execute_stmt_sig = network_result<
using execute_stmt_legacy_sig = network_result<
void>(conn_type&, const statement&, const std::tuple<field_view, field_view>&, results&);
using start_stmt_execution_tuple_sig = network_result<
using start_stmt_execution_legacy_tuple_sig = network_result<
void>(conn_type&, const statement&, const std::tuple<field_view, field_view>&, execution_state&);
using start_stmt_execution_it_sig =
using start_stmt_execution_legacy_it_sig =
network_result<void>(conn_type&, const statement&, fv_list_it, fv_list_it, execution_state&);
using execute_query_sig = network_result<void>(conn_type&, const string_view&, results&);
using execute_stmt_tuple_sig = network_result<void>(conn_type&, const stmt_tuple&, results&);
using execute_stmt_it_sig = network_result<void>(conn_type&, const stmt_it&, results&);
using start_execution_query_sig = network_result<void>(conn_type&, const string_view&, execution_state&);
using start_execution_stmt_tuple_sig =
network_result<void>(conn_type&, const stmt_tuple&, execution_state&);
using start_execution_stmt_it_sig = network_result<void>(conn_type&, const stmt_it&, execution_state&);
using close_stmt_sig = network_result<void>(conn_type&, const statement&);
using read_resultset_head_sig = network_result<void>(conn_type&, execution_state&);
using read_some_rows_sig = network_result<rows_view>(conn_type&, execution_state&);
@ -67,12 +77,18 @@ struct function_table
std::function<connect_sig> connect;
std::function<handshake_sig> handshake;
std::function<query_sig> query;
std::function<start_query_sig> start_query;
std::function<query_legacy_sig> query_legacy;
std::function<start_query_legacy_sig> start_query_legacy;
std::function<prepare_statement_sig> prepare_statement;
std::function<execute_stmt_sig> execute_stmt;
std::function<start_stmt_execution_tuple_sig> start_stmt_execution_tuple;
std::function<start_stmt_execution_it_sig> start_stmt_execution_it;
std::function<execute_stmt_legacy_sig> execute_stmt_legacy;
std::function<start_stmt_execution_legacy_tuple_sig> start_stmt_execution_legacy_tuple;
std::function<start_stmt_execution_legacy_it_sig> start_stmt_execution_legacy_it;
std::function<execute_query_sig> execute_query;
std::function<execute_stmt_tuple_sig> execute_stmt_tuple;
std::function<execute_stmt_it_sig> execute_stmt_it;
std::function<start_execution_query_sig> start_execution_query;
std::function<start_execution_stmt_tuple_sig> start_execution_stmt_tuple;
std::function<start_execution_stmt_it_sig> start_execution_stmt_it;
std::function<close_stmt_sig> close_stmt;
std::function<read_resultset_head_sig> read_resultset_head;
std::function<read_some_rows_sig> read_some_rows;
@ -96,12 +112,18 @@ function_table<Stream> create_sync_table()
return function_table<Stream>{
Netmaker::template type<typename table_t::connect_sig>::call(&conn_type::connect),
Netmaker::template type<typename table_t::handshake_sig>::call(&conn_type::handshake),
Netmaker::template type<typename table_t::query_sig>::call(&conn_type::query),
Netmaker::template type<typename table_t::start_query_sig>::call(&conn_type::start_query),
Netmaker::template type<typename table_t::query_legacy_sig>::call(&conn_type::query),
Netmaker::template type<typename table_t::start_query_legacy_sig>::call(&conn_type::start_query),
Netmaker::template type<typename table_t::prepare_statement_sig>::call(&conn_type::prepare_statement),
Netmaker::template type<typename table_t::execute_stmt_sig>::call(&conn_type::execute_statement),
Netmaker::template type<typename table_t::start_stmt_execution_tuple_sig>::call(&conn_type::start_statement_execution),
Netmaker::template type<typename table_t::start_stmt_execution_it_sig>::call(&conn_type::start_statement_execution),
Netmaker::template type<typename table_t::execute_stmt_legacy_sig>::call(&conn_type::execute_statement),
Netmaker::template type<typename table_t::start_stmt_execution_legacy_tuple_sig>::call(&conn_type::start_statement_execution),
Netmaker::template type<typename table_t::start_stmt_execution_legacy_it_sig>::call(&conn_type::start_statement_execution),
Netmaker::template type<typename table_t::execute_query_sig>::call(&conn_type::execute),
Netmaker::template type<typename table_t::execute_stmt_tuple_sig>::call(&conn_type::execute),
Netmaker::template type<typename table_t::execute_stmt_it_sig>::call(&conn_type::execute),
Netmaker::template type<typename table_t::start_execution_query_sig>::call(&conn_type::start_execution),
Netmaker::template type<typename table_t::start_execution_stmt_tuple_sig>::call(&conn_type::start_execution),
Netmaker::template type<typename table_t::start_execution_stmt_it_sig>::call(&conn_type::start_execution),
Netmaker::template type<typename table_t::close_stmt_sig>::call(&conn_type::close_statement),
Netmaker::template type<typename table_t::read_resultset_head_sig>::call(&conn_type::read_resultset_head),
Netmaker::template type<typename table_t::read_some_rows_sig>::call(&conn_type::read_some_rows),
@ -123,12 +145,18 @@ function_table<Stream> create_async_table()
return function_table<Stream>{
Netmaker::template type<typename table_t::connect_sig>::call(&conn_type::async_connect),
Netmaker::template type<typename table_t::handshake_sig>::call(&conn_type::async_handshake),
Netmaker::template type<typename table_t::query_sig>::call(&conn_type::async_query),
Netmaker::template type<typename table_t::start_query_sig>::call(&conn_type::async_start_query),
Netmaker::template type<typename table_t::query_legacy_sig>::call(&conn_type::async_query),
Netmaker::template type<typename table_t::start_query_legacy_sig>::call(&conn_type::async_start_query),
Netmaker::template type<typename table_t::prepare_statement_sig>::call(&conn_type::async_prepare_statement),
Netmaker::template type<typename table_t::execute_stmt_sig>::call(&conn_type::async_execute_statement),
Netmaker::template type<typename table_t::start_stmt_execution_tuple_sig>::call(&conn_type::async_start_statement_execution),
Netmaker::template type<typename table_t::start_stmt_execution_it_sig>::call(&conn_type::async_start_statement_execution),
Netmaker::template type<typename table_t::execute_stmt_legacy_sig>::call(&conn_type::async_execute_statement),
Netmaker::template type<typename table_t::start_stmt_execution_legacy_tuple_sig>::call(&conn_type::async_start_statement_execution),
Netmaker::template type<typename table_t::start_stmt_execution_legacy_it_sig>::call(&conn_type::async_start_statement_execution),
Netmaker::template type<typename table_t::execute_query_sig>::call(&conn_type::async_execute),
Netmaker::template type<typename table_t::execute_stmt_tuple_sig>::call(&conn_type::async_execute),
Netmaker::template type<typename table_t::execute_stmt_it_sig>::call(&conn_type::async_execute),
Netmaker::template type<typename table_t::start_execution_query_sig>::call(&conn_type::async_start_execution),
Netmaker::template type<typename table_t::start_execution_stmt_tuple_sig>::call(&conn_type::async_start_execution),
Netmaker::template type<typename table_t::start_execution_stmt_it_sig>::call(&conn_type::async_start_execution),
Netmaker::template type<typename table_t::close_stmt_sig>::call(&conn_type::async_close_statement),
Netmaker::template type<typename table_t::read_resultset_head_sig>::call(&conn_type::async_read_resultset_head),
Netmaker::template type<typename table_t::read_some_rows_sig>::call(&conn_type::async_read_some_rows),
@ -220,11 +248,11 @@ public:
}
network_result<void> query(string_view query, results& result) override
{
return table_.query(conn_, query, result);
return table_.query_legacy(conn_, query, result);
}
network_result<void> start_query(string_view query, execution_state& st) override
{
return table_.start_query(conn_, query, st);
return table_.start_query_legacy(conn_, query, st);
}
network_result<statement> prepare_statement(string_view stmt_sql) override
{
@ -237,7 +265,7 @@ public:
results& result
) override
{
return table_.execute_stmt(conn_, stmt, std::make_tuple(param1, param2), result);
return table_.execute_stmt_legacy(conn_, stmt, std::make_tuple(param1, param2), result);
}
network_result<void> start_statement_execution(
const statement& stmt,
@ -246,7 +274,7 @@ public:
execution_state& st
) override
{
return table_.start_stmt_execution_tuple(conn_, stmt, std::make_tuple(param1, param2), st);
return table_.start_stmt_execution_legacy_tuple(conn_, stmt, std::make_tuple(param1, param2), st);
}
network_result<void> start_statement_execution(
const statement& stmt,
@ -255,8 +283,41 @@ public:
execution_state& st
) override
{
return table_.start_stmt_execution_it(conn_, stmt, params_first, params_last, st);
return table_.start_stmt_execution_legacy_it(conn_, stmt, params_first, params_last, st);
}
network_result<void> execute(string_view query, results& result) override
{
return table_.execute_query(conn_, query, result);
}
network_result<void> execute(
bound_statement_tuple<std::tuple<field_view, field_view>> req,
results& result
) override
{
return table_.execute_stmt_tuple(conn_, req, result);
}
network_result<void> execute(bound_statement_iterator_range<fv_list_it> req, results& result) override
{
return table_.execute_stmt_it(conn_, req, result);
}
network_result<void> start_execution(string_view query, execution_state& st) override
{
return table_.start_execution_query(conn_, query, st);
}
network_result<void> start_execution(
bound_statement_tuple<std::tuple<field_view, field_view>> req,
execution_state& st
) override
{
return table_.start_execution_stmt_tuple(conn_, req, st);
}
network_result<void> start_execution(bound_statement_iterator_range<fv_list_it> req, execution_state& st)
override
{
return table_.start_execution_stmt_it(conn_, req, st);
}
network_result<void> close_statement(statement& stmt) override { return table_.close_stmt(conn_, stmt); }
network_result<void> read_resultset_head(execution_state& st) override
{

View File

@ -16,6 +16,7 @@ add_executable(
detail/auxiliar/static_string.cpp
detail/auxiliar/rows_iterator.cpp
detail/auxiliar/field_type_traits.cpp
detail/auxiliar/execution_request.cpp
detail/auxiliar/datetime.cpp
detail/auxiliar/row_impl.cpp
detail/protocol/capabilities.cpp
@ -28,10 +29,10 @@ add_executable(
detail/protocol/deserialize_execute_response.cpp
detail/protocol/process_error_packet.cpp
detail/protocol/execution_state_impl.cpp
detail/network_algorithms/start_execution_impl.cpp
detail/network_algorithms/read_resultset_head.cpp
detail/network_algorithms/start_execution.cpp
detail/network_algorithms/read_some_rows.cpp
detail/network_algorithms/execute.cpp
detail/network_algorithms/execute_impl.cpp
detail/network_algorithms/high_level_execution.cpp
detail/network_algorithms/close_statement.cpp
detail/network_algorithms/ping.cpp

View File

@ -35,6 +35,7 @@ unit-test boost_mysql_unittests
detail/auxiliar/static_string.cpp
detail/auxiliar/rows_iterator.cpp
detail/auxiliar/field_type_traits.cpp
detail/auxiliar/execution_request.cpp
detail/auxiliar/datetime.cpp
detail/auxiliar/row_impl.cpp
detail/protocol/capabilities.cpp
@ -47,10 +48,10 @@ unit-test boost_mysql_unittests
detail/protocol/deserialize_execute_response.cpp
detail/protocol/process_error_packet.cpp
detail/protocol/execution_state_impl.cpp
detail/network_algorithms/start_execution_impl.cpp
detail/network_algorithms/read_resultset_head.cpp
detail/network_algorithms/start_execution.cpp
detail/network_algorithms/read_some_rows.cpp
detail/network_algorithms/execute.cpp
detail/network_algorithms/execute_impl.cpp
detail/network_algorithms/high_level_execution.cpp
detail/network_algorithms/close_statement.cpp
detail/network_algorithms/ping.cpp

View File

@ -0,0 +1,76 @@
//
// Copyright (c) 2019-2023 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/string_view.hpp>
#include <boost/mysql/detail/auxiliar/execution_request.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/impl/statement.hpp>
#include <string>
#ifdef __cpp_lib_string_view
#include <string_view>
#endif
using namespace boost::mysql;
using detail::is_execution_request;
// -----
// strings
// -----
static_assert(is_execution_request<std::string>::value, "");
static_assert(is_execution_request<const std::string&>::value, "");
static_assert(is_execution_request<std::string&&>::value, "");
static_assert(is_execution_request<std::string&>::value, "");
static_assert(is_execution_request<string_view>::value, "");
static_assert(is_execution_request<const string_view&>::value, "");
static_assert(is_execution_request<string_view&&>::value, "");
#ifdef __cpp_lib_string_view
static_assert(is_execution_request<std::string_view>::value, "");
static_assert(is_execution_request<const std::string_view&>::value, "");
static_assert(is_execution_request<std::string_view&&>::value, "");
#endif
static_assert(is_execution_request<const char*>::value, "");
static_assert(is_execution_request<const char[14]>::value, "");
static_assert(is_execution_request<const char (&)[14]>::value, "");
// -----
// tuple statements
// -----
using tup_type = std::tuple<int, const char*, std::nullptr_t>;
static_assert(is_execution_request<bound_statement_tuple<tup_type>>::value, "");
static_assert(is_execution_request<const bound_statement_tuple<tup_type>&>::value, "");
static_assert(is_execution_request<bound_statement_tuple<tup_type>&>::value, "");
static_assert(is_execution_request<bound_statement_tuple<tup_type>&&>::value, "");
static_assert(is_execution_request<bound_statement_tuple<std::tuple<>>>::value, "");
// tuples not accepted
static_assert(!is_execution_request<tup_type>::value, "");
static_assert(!is_execution_request<const tup_type&>::value, "");
static_assert(!is_execution_request<tup_type&>::value, "");
static_assert(!is_execution_request<tup_type&&>::value, "");
// -----
// iterator statements
// -----
static_assert(is_execution_request<bound_statement_iterator_range<field_view*>>::value, "");
static_assert(is_execution_request<const bound_statement_iterator_range<field_view*>&>::value, "");
static_assert(is_execution_request<bound_statement_iterator_range<field_view*>&>::value, "");
static_assert(is_execution_request<bound_statement_iterator_range<field_view*>&&>::value, "");
// iterators not accepted
static_assert(!is_execution_request<field_view*>::value, "");
// Other stuff not accepted
static_assert(!is_execution_request<field_view>::value, "");
static_assert(!is_execution_request<int>::value, "");
static_assert(!is_execution_request<std::nullptr_t>::value, "");

View File

@ -8,7 +8,7 @@
#include <boost/mysql/client_errc.hpp>
#include <boost/mysql/execution_state.hpp>
#include <boost/mysql/detail/network_algorithms/execute.hpp>
#include <boost/mysql/detail/network_algorithms/execute_impl.hpp>
#include <boost/mysql/detail/protocol/constants.hpp>
#include <boost/mysql/detail/protocol/resultset_encoding.hpp>
@ -38,8 +38,8 @@ struct
typename netfun_maker::signature execute;
const char* name;
} all_fns[] = {
{netfun_maker::sync_errc(&detail::execute<test_stream>), "sync" },
{netfun_maker::async_errinfo(&detail::async_execute<test_stream>), "async"}
{netfun_maker::sync_errc(&detail::execute_impl<test_stream>), "sync" },
{netfun_maker::async_errinfo(&detail::async_execute_impl<test_stream>), "async"}
};
// Verify that we clear any previous result

View File

@ -11,6 +11,7 @@
#include <boost/mysql/field_view.hpp>
#include <boost/mysql/results.hpp>
#include <boost/mysql/statement.hpp>
#include <boost/mysql/string_view.hpp>
#include <boost/mysql/detail/protocol/constants.hpp>
#include <boost/mysql/detail/protocol/resultset_encoding.hpp>
@ -19,7 +20,9 @@
#include <boost/asio/use_awaitable.hpp>
#include <boost/test/unit_test.hpp>
#include <cstddef>
#include <tuple>
#include <type_traits>
#include "assert_buffer_equals.hpp"
#include "creation/create_execution_state.hpp"
@ -78,20 +81,27 @@ execution_state create_initial_state()
//
// ------- query ---------
// Both legacy query() and execute("SELECT ...") share a compatible signature
//
BOOST_AUTO_TEST_SUITE(query_)
using netfun_maker = netfun_maker_mem<void, test_connection, string_view, results&>;
using netfun_maker_execute = netfun_maker_mem<void, test_connection, const string_view&, results&>;
struct
{
netfun_maker::signature query;
const char* name;
} all_fns[] = {
{netfun_maker::sync_errc(&test_connection::query), "sync_errc" },
{netfun_maker::sync_exc(&test_connection::query), "sync_exc" },
{netfun_maker::async_errinfo(&test_connection::async_query), "async_errinfo" },
{netfun_maker::async_noerrinfo(&test_connection::async_query), "async_noerrinfo"},
{netfun_maker::sync_errc(&test_connection::query), "sync_errc" },
{netfun_maker::sync_exc(&test_connection::query), "sync_exc" },
{netfun_maker::async_errinfo(&test_connection::async_query), "async_errinfo" },
{netfun_maker::async_noerrinfo(&test_connection::async_query), "async_noerrinfo"},
{netfun_maker_execute::sync_errc(&test_connection::execute), "sync_errc" },
{netfun_maker_execute::sync_exc(&test_connection::execute), "sync_exc" },
{netfun_maker_execute::async_errinfo(&test_connection::async_execute), "async_errinfo" },
{netfun_maker_execute::async_noerrinfo(&test_connection::async_execute), "async_noerrinfo"},
};
BOOST_AUTO_TEST_CASE(success)
@ -140,20 +150,27 @@ BOOST_AUTO_TEST_SUITE_END()
//
// ------- start_query ---------
// Both legacy start_query() and start_execution("SELECT ...") share a compatible signature
//
BOOST_AUTO_TEST_SUITE(start_query_)
using netfun_maker = netfun_maker_mem<void, test_connection, string_view, execution_state&>;
using netfun_maker_execute = netfun_maker_mem<void, test_connection, const string_view&, execution_state&>;
struct
{
netfun_maker::signature start_query;
const char* name;
} all_fns[] = {
{netfun_maker::sync_errc(&test_connection::start_query), "sync_errc" },
{netfun_maker::sync_exc(&test_connection::start_query), "sync_exc" },
{netfun_maker::async_errinfo(&test_connection::async_start_query), "async_errinfo" },
{netfun_maker::async_noerrinfo(&test_connection::async_start_query), "async_noerrinfo"},
{netfun_maker::sync_errc(&test_connection::start_query), "sync_errc" },
{netfun_maker::sync_exc(&test_connection::start_query), "sync_exc" },
{netfun_maker::async_errinfo(&test_connection::async_start_query), "async_errinfo" },
{netfun_maker::async_noerrinfo(&test_connection::async_start_query), "async_noerrinfo"},
{netfun_maker_execute::sync_errc(&test_connection::start_execution), "sync_errc" },
{netfun_maker_execute::sync_exc(&test_connection::start_execution), "sync_exc" },
{netfun_maker_execute::async_errinfo(&test_connection::async_start_execution), "async_errinfo" },
{netfun_maker_execute::async_noerrinfo(&test_connection::async_start_execution), "async_noerrinfo"},
};
BOOST_AUTO_TEST_CASE(success)
@ -200,13 +217,12 @@ BOOST_AUTO_TEST_CASE(error)
}
}
}
BOOST_AUTO_TEST_SUITE_END()
//
// ------- execute_statement ---------
// ------- execute_statement (legacy) ---------
//
BOOST_AUTO_TEST_SUITE(execute_statement_)
BOOST_AUTO_TEST_SUITE(execute_statement_legacy)
using netfun_maker = netfun_maker_mem<
void,
@ -321,9 +337,219 @@ BOOST_AUTO_TEST_CASE(deferred_lifetimes_lvalues)
BOOST_AUTO_TEST_SUITE_END()
//
// ------- start_statement_execution (tuple) ---------
// ------- execute_statement (tuple) ---------
//
BOOST_AUTO_TEST_SUITE(start_statement_execution_tuple)
BOOST_AUTO_TEST_SUITE(execute_statement_tuple)
using netfun_maker = netfun_maker_mem<
void,
test_connection,
const bound_statement_tuple<std::tuple<const char*, std::nullptr_t>>&,
results&>;
struct
{
netfun_maker::signature execute;
const char* name;
} all_fns[] = {
{netfun_maker::sync_errc(&test_connection::execute), "sync_errc" },
{netfun_maker::sync_exc(&test_connection::execute), "sync_exc" },
{netfun_maker::async_errinfo(&test_connection::async_execute), "async_errinfo" },
{netfun_maker::async_noerrinfo(&test_connection::async_execute), "async_noerrinfo"},
};
BOOST_AUTO_TEST_CASE(success)
{
for (auto fns : all_fns)
{
BOOST_TEST_CONTEXT(fns.name)
{
auto result = create_initial_results();
auto stmt = create_the_statement();
test_connection conn;
conn.stream().add_message(ok_msg_builder().seqnum(1).affected_rows(50).info("1st").build_ok());
// Call the function
fns.execute(conn, stmt.bind("test", nullptr), result).validate_no_error();
// Verify the message we sent
BOOST_MYSQL_ASSERT_BLOB_EQUALS(conn.stream().bytes_written(), execute_stmt_msg);
// Verify the results
BOOST_TEST_REQUIRE(result.size() == 1u);
BOOST_TEST(result.meta().size() == 0u);
BOOST_TEST(result.affected_rows() == 50u);
BOOST_TEST(result.info() == "1st");
}
}
}
BOOST_AUTO_TEST_CASE(error_wrong_num_params)
{
for (auto fns : all_fns)
{
BOOST_TEST_CONTEXT(fns.name)
{
auto result = create_initial_results();
auto stmt = statement_builder().id(1).num_params(3).build();
test_connection conn;
// Call the function
fns.execute(conn, stmt.bind("test", nullptr), result)
.validate_error_exact(client_errc::wrong_num_params);
}
}
}
// Verify that we correctly perform a decay-copy of the execution request,
// relevant for deferred tokens
#ifdef BOOST_ASIO_HAS_CO_AWAIT
BOOST_AUTO_TEST_CASE(deferred_lifetimes_rvalues)
{
run_coroutine([]() -> boost::asio::awaitable<void> {
results result;
test_connection conn;
conn.stream().add_message(ok_msg_builder().seqnum(1).info("1st").build_ok());
// Deferred op. Execution request is a temporary
auto aw = conn.async_execute(
create_the_statement().bind(std::string("test"), nullptr),
result,
boost::asio::use_awaitable
);
co_await std::move(aw);
// verify that the op had the intended effects
BOOST_MYSQL_ASSERT_BLOB_EQUALS(conn.stream().bytes_written(), execute_stmt_msg);
BOOST_TEST(result.info() == "1st");
});
}
BOOST_AUTO_TEST_CASE(deferred_lifetimes_lvalues)
{
run_coroutine([]() -> boost::asio::awaitable<void> {
results result;
test_connection conn;
conn.stream().add_message(ok_msg_builder().seqnum(1).info("1st").build_ok());
boost::asio::awaitable<void> aw;
// Deferred op
{
auto stmt = create_the_statement();
auto req = stmt.bind(std::string("test"), nullptr);
aw = conn.async_execute(req, result, boost::asio::use_awaitable);
}
co_await std::move(aw);
// verify that the op had the intended effects
BOOST_MYSQL_ASSERT_BLOB_EQUALS(conn.stream().bytes_written(), execute_stmt_msg);
BOOST_TEST(result.info() == "1st");
});
}
#endif
BOOST_AUTO_TEST_SUITE_END()
//
// ------- execute_statement (iterator) ---------
//
BOOST_AUTO_TEST_SUITE(execute_statement_iterator_range)
using netfun_maker = netfun_maker_mem<
void,
test_connection,
const bound_statement_iterator_range<const field_view*>&,
results&>;
struct
{
netfun_maker::signature execute;
const char* name;
} all_fns[] = {
{netfun_maker::sync_errc(&test_connection::execute), "sync_errc" },
{netfun_maker::sync_exc(&test_connection::execute), "sync_exc" },
{netfun_maker::async_errinfo(&test_connection::async_execute), "async_errinfo" },
{netfun_maker::async_noerrinfo(&test_connection::async_execute), "async_noerrinfo"},
};
BOOST_AUTO_TEST_CASE(success)
{
for (auto fns : all_fns)
{
BOOST_TEST_CONTEXT(fns.name)
{
auto result = create_initial_results();
auto stmt = create_the_statement();
test_connection conn;
conn.stream().add_message(ok_msg_builder().seqnum(1).affected_rows(50).info("1st").build_ok());
// Call the function
const auto params = make_fv_arr("test", nullptr);
fns.execute(conn, stmt.bind(params.data(), params.data() + params.size()), result)
.validate_no_error();
// Verify the message we sent
BOOST_MYSQL_ASSERT_BLOB_EQUALS(conn.stream().bytes_written(), execute_stmt_msg);
// Verify the results
BOOST_TEST_REQUIRE(result.size() == 1u);
BOOST_TEST(result.meta().size() == 0u);
BOOST_TEST(result.affected_rows() == 50u);
BOOST_TEST(result.info() == "1st");
}
}
}
// Regression check: iterator reference type is convertible to field_view,
// but not equal to field_view
BOOST_AUTO_TEST_CASE(iterator_reference_not_field_view)
{
auto result = create_initial_results();
auto stmt = create_the_statement();
test_connection conn;
conn.stream().add_message(ok_msg_builder().seqnum(1).affected_rows(50).info("1st").build_ok());
// Call the function
std::vector<field> fields{field_view("test"), field_view()};
conn.execute(stmt.bind(fields.begin(), fields.end()), result);
// Verify the message we sent
BOOST_MYSQL_ASSERT_BLOB_EQUALS(conn.stream().bytes_written(), execute_stmt_msg);
// Verify the results
BOOST_TEST_REQUIRE(result.size() == 1u);
BOOST_TEST(result.meta().size() == 0u);
BOOST_TEST(result.affected_rows() == 50u);
BOOST_TEST(result.info() == "1st");
}
BOOST_AUTO_TEST_CASE(error_wrong_num_params)
{
for (auto fns : all_fns)
{
BOOST_TEST_CONTEXT(fns.name)
{
auto result = create_initial_results();
auto stmt = statement_builder().id(1).num_params(3).build();
test_connection conn;
// Call the function
const auto params = make_fv_arr("test", nullptr);
fns.execute(conn, stmt.bind(params.data(), params.data() + params.size()), result)
.validate_error_exact(client_errc::wrong_num_params);
}
}
}
// No need to test for decay copies again here, already tested
// in the tuple version.
BOOST_AUTO_TEST_SUITE_END()
//
// ------- start_statement_execution (tuple, legacy) ---------
//
BOOST_AUTO_TEST_SUITE(start_statement_execution_tuple_legacy)
using netfun_maker = netfun_maker_mem<
void,
@ -441,9 +667,126 @@ BOOST_AUTO_TEST_CASE(deferred_lifetimes_lvalues)
BOOST_AUTO_TEST_SUITE_END()
//
// ------- start_statement_execution (iterator) ---------
// ------- start_statement_execution (tuple) ---------
//
BOOST_AUTO_TEST_SUITE(start_statement_execution_it)
BOOST_AUTO_TEST_SUITE(start_statement_execution_tuple)
using netfun_maker = netfun_maker_mem<
void,
test_connection,
const bound_statement_tuple<std::tuple<const char*, std::nullptr_t>>&,
execution_state&>;
struct
{
netfun_maker::signature start_execution;
const char* name;
} all_fns[] = {
{netfun_maker::sync_errc(&test_connection::start_execution), "sync_errc" },
{netfun_maker::sync_exc(&test_connection::start_execution), "sync_exc" },
{netfun_maker::async_errinfo(&test_connection::async_start_execution), "async_errinfo" },
{netfun_maker::async_noerrinfo(&test_connection::async_start_execution), "async_noerrinfo"},
};
BOOST_AUTO_TEST_CASE(success)
{
for (auto fns : all_fns)
{
BOOST_TEST_CONTEXT(fns.name)
{
auto st = create_initial_state();
auto stmt = create_the_statement();
test_connection conn;
conn.stream().add_message(ok_msg_builder().seqnum(1).affected_rows(50).info("1st").build_ok());
// Call the function
fns.start_execution(conn, stmt.bind("test", nullptr), st).validate_no_error();
// Verify the message we sent
BOOST_MYSQL_ASSERT_BLOB_EQUALS(conn.stream().bytes_written(), execute_stmt_msg);
// Verify the results
BOOST_TEST(get_impl(st).encoding() == resultset_encoding::binary);
BOOST_TEST_REQUIRE(st.complete());
BOOST_TEST(get_impl(st).sequence_number() == 2u);
BOOST_TEST(st.meta().size() == 0u);
BOOST_TEST(st.affected_rows() == 50u);
BOOST_TEST(st.info() == "1st");
}
}
}
BOOST_AUTO_TEST_CASE(error_wrong_num_params)
{
for (auto fns : all_fns)
{
BOOST_TEST_CONTEXT(fns.name)
{
execution_state st;
auto stmt = statement_builder().id(1).num_params(3).build();
test_connection conn;
// Call the function
fns.start_execution(conn, stmt.bind("test", nullptr), st)
.validate_error_exact(client_errc::wrong_num_params);
}
}
}
// Verify that we correctly perform a decay-copy of the parameters and the
// statement handle, relevant for deferred tokens
#ifdef BOOST_ASIO_HAS_CO_AWAIT
BOOST_AUTO_TEST_CASE(deferred_lifetimes_rvalues)
{
run_coroutine([]() -> boost::asio::awaitable<void> {
execution_state st;
test_connection conn;
conn.stream().add_message(ok_msg_builder().seqnum(1).info("1st").build_ok());
// Deferred op. Execution request is a temporary
auto aw = conn.async_start_execution(
create_the_statement().bind(std::string("test"), nullptr),
st,
boost::asio::use_awaitable
);
co_await std::move(aw);
// verify that the op had the intended effects
BOOST_MYSQL_ASSERT_BLOB_EQUALS(conn.stream().bytes_written(), execute_stmt_msg);
BOOST_TEST(st.info() == "1st");
});
}
BOOST_AUTO_TEST_CASE(deferred_lifetimes_lvalues)
{
run_coroutine([]() -> boost::asio::awaitable<void> {
execution_state st;
test_connection conn;
conn.stream().add_message(ok_msg_builder().seqnum(1).info("1st").build_ok());
boost::asio::awaitable<void> aw;
// Deferred op
{
const auto stmt = create_the_statement();
const auto req = stmt.bind(std::string("test"), nullptr);
aw = conn.async_start_execution(req, st, boost::asio::use_awaitable);
}
co_await std::move(aw);
// verify that the op had the intended effects
BOOST_MYSQL_ASSERT_BLOB_EQUALS(conn.stream().bytes_written(), execute_stmt_msg);
BOOST_TEST(st.info() == "1st");
});
}
#endif
BOOST_AUTO_TEST_SUITE_END()
//
// ------- start_statement_execution (iterator, legacy) ---------
//
BOOST_AUTO_TEST_SUITE(start_statement_execution_it_legacy)
using netfun_maker = netfun_maker_mem<
void,
@ -541,6 +884,80 @@ BOOST_AUTO_TEST_CASE(deferred_lifetimes)
BOOST_AUTO_TEST_SUITE_END()
//
// ------- start_statement_execution (iterator) ---------
//
BOOST_AUTO_TEST_SUITE(start_statement_execution_it)
using netfun_maker = netfun_maker_mem<
void,
test_connection,
const bound_statement_iterator_range<field_view*>&,
execution_state&>;
struct
{
netfun_maker::signature start_execution;
const char* name;
} all_fns[] = {
{netfun_maker::sync_errc(&test_connection::start_execution), "sync_errc" },
{netfun_maker::sync_exc(&test_connection::start_execution), "sync_exc" },
{netfun_maker::async_errinfo(&test_connection::async_start_execution), "async_errinfo" },
{netfun_maker::async_noerrinfo(&test_connection::async_start_execution), "async_noerrinfo"},
};
BOOST_AUTO_TEST_CASE(success)
{
for (auto fns : all_fns)
{
BOOST_TEST_CONTEXT(fns.name)
{
auto st = create_initial_state();
auto stmt = create_the_statement();
test_connection conn;
conn.stream().add_message(ok_msg_builder().seqnum(1).affected_rows(50).info("1st").build_ok());
// Call the function
auto fields = make_fv_arr("test", nullptr);
fns.start_execution(conn, stmt.bind(fields.data(), fields.data() + fields.size()), st)
.validate_no_error();
// Verify the message we sent
BOOST_MYSQL_ASSERT_BLOB_EQUALS(conn.stream().bytes_written(), execute_stmt_msg);
// Verify the results
BOOST_TEST(get_impl(st).encoding() == resultset_encoding::binary);
BOOST_TEST(st.complete());
BOOST_TEST(get_impl(st).sequence_number() == 2u);
BOOST_TEST(st.meta().size() == 0u);
BOOST_TEST(st.affected_rows() == 50u);
BOOST_TEST(st.info() == "1st");
}
}
}
BOOST_AUTO_TEST_CASE(error_wrong_num_params)
{
for (auto fns : all_fns)
{
BOOST_TEST_CONTEXT(fns.name)
{
execution_state st;
auto stmt = statement_builder().id(1).num_params(3).build();
test_connection conn;
// Call the function
auto fields = make_fv_arr("test", nullptr);
fns.start_execution(conn, stmt.bind(fields.data(), fields.data() + fields.size()), st)
.validate_error_exact(client_errc::wrong_num_params);
}
}
}
// No need to test for decay copies again here, already tested
// in the tuple version.
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()
} // namespace

View File

@ -12,7 +12,7 @@
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/field_view.hpp>
#include <boost/mysql/detail/network_algorithms/start_execution.hpp>
#include <boost/mysql/detail/network_algorithms/start_execution_impl.hpp>
#include <boost/mysql/detail/protocol/constants.hpp>
#include <boost/mysql/detail/protocol/execution_state_impl.hpp>
#include <boost/mysql/detail/protocol/resultset_encoding.hpp>
@ -47,8 +47,8 @@ struct
typename netfun_maker::signature start_execution;
const char* name;
} all_fns[] = {
{netfun_maker::sync_errc(&boost::mysql::detail::start_execution), "sync" },
{netfun_maker::async_errinfo(&boost::mysql::detail::async_start_execution), "async"}
{netfun_maker::sync_errc(&boost::mysql::detail::start_execution_impl), "sync" },
{netfun_maker::async_errinfo(&boost::mysql::detail::async_start_execution_impl), "async"}
};
BOOST_AUTO_TEST_SUITE(test_start_execution)

View File

@ -32,22 +32,22 @@ using detail::protocol_field_type;
namespace {
using start_query_netm = netfun_maker_mem<void, test_connection, string_view, execution_state&>;
using start_query_netm = netfun_maker_mem<void, test_connection, const string_view&, execution_state&>;
using read_resultset_head_netm = netfun_maker_mem<void, test_connection, execution_state&>;
using read_some_rows_netm = netfun_maker_mem<rows_view, test_connection, execution_state&>;
struct
{
start_query_netm::signature start_query;
start_query_netm::signature start_execution;
read_resultset_head_netm::signature read_resultset_head;
read_some_rows_netm::signature read_some_rows;
const char* name;
} all_fns[] = {
{start_query_netm::sync_errc(&test_connection::start_query),
{start_query_netm::sync_errc(&test_connection::start_execution),
read_resultset_head_netm::sync_errc(&test_connection::read_resultset_head),
read_some_rows_netm::sync_errc(&test_connection::read_some_rows),
"sync" },
{start_query_netm::async_errinfo(&test_connection::async_start_query),
{start_query_netm::async_errinfo(&test_connection::async_start_execution),
read_resultset_head_netm::async_errinfo(&test_connection::async_read_resultset_head),
read_some_rows_netm::async_errinfo(&test_connection::async_read_some_rows),
"async"},
@ -79,7 +79,7 @@ BOOST_AUTO_TEST_CASE(separate_batches)
));
// Start
fns.start_query(conn, "SELECT 1", st).validate_no_error();
fns.start_execution(conn, "SELECT 1", st).validate_no_error();
BOOST_TEST_REQUIRE(st.should_read_rows());
check_meta(st.meta(), {column_type::varchar});
@ -141,7 +141,7 @@ BOOST_AUTO_TEST_CASE(single_read)
conn.stream().add_message(std::move(msgs));
// Start
fns.start_query(conn, "SELECT 1", st).validate_no_error();
fns.start_execution(conn, "SELECT 1", st).validate_no_error();
BOOST_TEST_REQUIRE(st.should_read_rows());
check_meta(st.meta(), {column_type::varchar});
@ -188,7 +188,7 @@ BOOST_AUTO_TEST_CASE(empty_resultsets)
conn.stream().add_message(std::move(msgs));
// Start
fns.start_query(conn, "SELECT 1", st).validate_no_error();
fns.start_execution(conn, "SELECT 1", st).validate_no_error();
BOOST_TEST_REQUIRE(st.should_read_head());
BOOST_TEST(st.meta().size() == 0u);
BOOST_TEST(st.affected_rows() == 10u);

View File

@ -5,15 +5,18 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include <boost/mysql/field_view.hpp>
#include <boost/mysql/statement.hpp>
#include <boost/test/unit_test.hpp>
#include <tuple>
#include "creation/create_statement.hpp"
#include "test_common.hpp"
using namespace boost::mysql::test;
using boost::mysql::statement;
using namespace boost::mysql;
namespace {
@ -34,6 +37,84 @@ BOOST_AUTO_TEST_CASE(member_fns)
BOOST_TEST(stmt.id() == 1u);
}
statement create_valid_stmt() { return statement_builder().id(1).build(); }
BOOST_AUTO_TEST_SUITE(bind_field_like)
BOOST_AUTO_TEST_CASE(regular)
{
std::string s("def");
blob blb;
auto b = create_valid_stmt().bind(42, std::string("abc"), std::ref(s), s, blb);
using tup_type = std::tuple<int, std::string, std::string&, std::string, blob>;
static_assert(std::is_same<decltype(b), bound_statement_tuple<tup_type>>::value, "");
}
BOOST_AUTO_TEST_CASE(empty)
{
auto b = create_valid_stmt().bind();
static_assert(std::is_same<decltype(b), bound_statement_tuple<std::tuple<>>>::value, "");
}
BOOST_AUTO_TEST_CASE(stmt_const)
{
const statement stmt = create_valid_stmt();
auto b = stmt.bind(); // compiles
static_assert(std::is_same<decltype(b), bound_statement_tuple<std::tuple<>>>::value, "");
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE(bind_field_like_tuple)
BOOST_AUTO_TEST_CASE(regular)
{
auto b = create_valid_stmt().bind(std::make_tuple(42, 4.2f));
using expected_type = bound_statement_tuple<std::tuple<int, float>>;
static_assert(std::is_same<decltype(b), expected_type>::value, "");
}
BOOST_AUTO_TEST_CASE(tuple_const_reference)
{
const auto params = std::make_tuple(42, 4.2f);
auto b = create_valid_stmt().bind(params);
using expected_type = bound_statement_tuple<std::tuple<int, float>>;
static_assert(std::is_same<decltype(b), expected_type>::value, "");
}
BOOST_AUTO_TEST_CASE(tuple_reference)
{
auto params = std::make_tuple(42, 4.2f);
auto b = create_valid_stmt().bind(params);
using expected_type = bound_statement_tuple<std::tuple<int, float>>;
static_assert(std::is_same<decltype(b), expected_type>::value, "");
}
BOOST_AUTO_TEST_CASE(stmt_const)
{
const statement stmt = create_valid_stmt();
auto b = stmt.bind(std::make_tuple(42)); // compiles
using expected_type = bound_statement_tuple<std::tuple<int>>;
static_assert(std::is_same<decltype(b), expected_type>::value, "");
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE(bind_iterator_range)
BOOST_AUTO_TEST_CASE(regular)
{
auto fvs = make_fv_arr(42, "abc");
auto b = create_valid_stmt().bind(fvs.begin(), fvs.end());
using expected_type = bound_statement_iterator_range<std::array<field_view, 2>::iterator>;
static_assert(std::is_same<decltype(b), expected_type>::value, "");
}
BOOST_AUTO_TEST_CASE(stmt_const)
{
const statement stmt = create_valid_stmt();
auto fvs = make_fv_arr(42, "abc");
auto b = stmt.bind(fvs.begin(), fvs.end()); // compiles
using expected_type = bound_statement_iterator_range<std::array<field_view, 2>::iterator>;
static_assert(std::is_same<decltype(b), expected_type>::value, "");
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()
} // namespace