diff --git a/.gitignore b/.gitignore
index 1b5c6dfb..a6fcfb12 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,5 @@ out/
doc/html/
doc/qbk/reference.qbk
.vscode/
+compile_commands.json
+.cache/
diff --git a/doc/Doxyfile b/doc/Doxyfile
index 9bafb357..de1b8077 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -16,7 +16,7 @@ INPUT_ENCODING = UTF-8
FILE_PATTERNS =
RECURSIVE = NO
EXCLUDE = $(LIB_DIR)/include/boost/mysql/detail/ \
- $(LIB_DIR)/include/boost/mysql/impl
+ $(LIB_DIR)/include/boost/mysql/impl/
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS =
EXCLUDE_SYMBOLS =
@@ -36,16 +36,16 @@ USE_MDFILE_AS_MAINPAGE =
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = YES
-SEARCH_INCLUDES = YES
+SEARCH_INCLUDES = NO
PREDEFINED = BOOST_MYSQL_DOXYGEN \
- BOOST_ASIO_INITFN_RESULT_TYPE(t,a)=auto \
- BOOST_ASIO_COMPLETION_TOKEN_FOR(sig)=class \
- BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(ct,sig)=auto \
- BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(ex)= \
- BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(ex)= \
- protected=private \
- BOOST_CXX14_CONSTEXPR=constexpr
+ "BOOST_ASIO_INITFN_RESULT_TYPE(t,a)=auto" \
+ "BOOST_ASIO_COMPLETION_TOKEN_FOR(sig)=class" \
+ "BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(ct,sig)=auto" \
+ "BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(ex)=" \
+ "BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(ex)=" \
+ "protected=private" \
+ "BOOST_CXX14_CONSTEXPR=constexpr"
SKIP_FUNCTION_MACROS = NO
@@ -189,7 +189,7 @@ HAVE_DOT = NO
#---------------------------------------------------------------------------
GENERATE_XML = YES
XML_OUTPUT = $(XML_OUTPUT)
-XML_PROGRAMLISTING = YES
+XML_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
# Configuration options related to external references
@@ -199,4 +199,3 @@ GENERATE_TAGFILE =
ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES
EXTERNAL_PAGES = YES
-PERL_PATH = /usr/bin/perl
diff --git a/doc/Jamfile b/doc/Jamfile
index 46570e72..5dcf5426 100644
--- a/doc/Jamfile
+++ b/doc/Jamfile
@@ -97,7 +97,8 @@ make base-extract-xml-pages.xsl : $(docca)/include/docca/base-extract-xml-pages.
make common.xsl : $(docca)/include/docca/common.xsl : @copy_script ;
make stage1.xsl : $(docca)/include/docca/stage1.xsl : @copy_script ;
make base-stage1.xsl : $(docca)/include/docca/base-stage1.xsl : @copy_script ;
-make stage2.xsl : $(docca)/include/docca/stage2.xsl : @copy_script ;
+# Workaround to prevent docca from escaping quickbook from Doxygen comments
+make stage2.xsl : $(BOOST_ROOT)/libs/mysql/doc/docca-stage2-noescape.xsl : @copy_script ;
make assemble-quickbook.xsl : $(docca)/include/docca/assemble-quickbook.xsl : @copy_script ;
make emphasized-types.xsl : $(docca)/include/docca/emphasized-types.xsl : @copy_script ;
diff --git a/doc/docca-stage2-noescape.xsl b/doc/docca-stage2-noescape.xsl
new file mode 100644
index 00000000..e9d78cf3
--- /dev/null
+++ b/doc/docca-stage2-noescape.xsl
@@ -0,0 +1,224 @@
+
+]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {$nl}
+ [section:{tokenize(@id,'\.')[last()]} {d:qb-escape(title)}]
+
+
+
+
+ {$nl}[indexterm1 {d:qb-escape(@primary-index-term)}]{$nl}
+ {$nl}[indexterm2 {d:qb-escape(@primary-index-term)}..{
+ d:qb-escape(@secondary-index-term)}]{$nl}
+
+
+
+
+ {$nl}[heading {.}]
+
+
+
+
+
+
+
+
+
+ {$nl}```{$nl}
+ {$nl}```{$nl}
+
+
+
+ {$nl}{$nl}
+
+ {$nl}
+
+ ;
+
+
+ ;{$nl}
+
+ '''»''' [more...]
+
+ ``
+
+ ``
+
+
+ ``[link {$doc-ref}.{@d:refid} {d:qb-escape(.)}]``
+ [link {$doc-ref}.{@d:refid} {d:qb-escape(.)}]
+ [link {$doc-ref}.{@d:refid} `{d:qb-escape(.)}`]
+
+ `
+ `
+
+ enum
+
+ using
+ =
+ ;
+
+ ``['implementation-defined]``
+ ``['see-below]``
+ ``__deduced__``
+
+ {' '}
+ ;
+
+ {' '}
+
+ :{$nl}
+ {$nl}
+ ,
+
+ {' '}
+
+ template<
+ >{$nl}
+
+ {$nl}
+ ,
+
+ {' '}
+
+
+ (
+ )
+
+ __{translate(.,'_','')}__
+
+ =
+
+ {' '}
+
+
+ {$nl}
+
+
+ [role red error.{@message}]
+
+ {$nl}[table
+ {$nl}]
+
+
+ [
+ ]
+
+ {$nl} [
+ {$nl} ]
+
+ {$nl} [
+ {$nl} ]
+
+ [*
+ ]
+
+ ['
+ ]
+
+ [@{@url}
+ ]
+
+ {$nl}
+
+
+ {$nl}
+
+
+
+
+
+
+
+
+ {ancestor::listitem ! (1 to $list-indent-width) ! ' '}
+
+ *
+ #
+
+
+
+ ['
+ [role red \[Page type: [*{/*/@type}]\]]
+ [role green \[[@../../doc/html/{translate($doc-ref,'.','/')}/{
+ translate(/page/@id,'.','/')}.html [role green doc_build_html]]\]]
+ [@../build/xml-pages/{/page/@id}.xml [role blue [*\[doxygen_page_xml\]]]]
+ [@../build/stage1_visualized/visualized/{/page/@id}.html [role magenta ---stage1_visualized-->]]
+ [@../build/stage1_visualized/results/{ /page/@id}.xml [role blue [*\[docca_page_xml\]]]]
+ [@../build/stage2_visualized/visualized/{/page/@id}.html [role magenta ---stage2_visualized-->]]
+ [@../build/stage2_visualized/results/{ /page/@id}.txt [role blue [*\[quickbook_result\]]]]
+ ]
+
+
+
+ {$nl}
+
+ {' '}
+
+ {$nl}{$nl}
+
+ {$nl}```{$nl}
+ ```{$nl}
+
+ {$nl}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/doc/qbk/resultsets.qbk b/doc/qbk/resultsets.qbk
index c494bb2f..8ea800f9 100644
--- a/doc/qbk/resultsets.qbk
+++ b/doc/qbk/resultsets.qbk
@@ -16,11 +16,9 @@ corresponding asynchronous versions.
[section:rows Rows]
A [reflink resultset] represents tabular data. Data is represented
-row-by-row. Depending on which method you use to retrieve the rows,
-these may be [reflink row] or [reflink owning_row] objects.
-In essence, both represent an array of [reflink value]s, which
-you can retrieve using [refmem row values]. This array will
-have a single element for each field (column) in the [reflink resultset].
+row-by-row, using the [reflink row] class, which represent an array
+of [reflink value]s, which you can retrieve using [refmem row values].
+This array will have a single element for each field (column) in the [reflink resultset].
Fields appear in the resultset in the same order as in the SQL query.
For example, let `rowobj` an [reflink row] obtained from a [reflink resultset]
@@ -44,70 +42,54 @@ directly. Instead, [reflink resultset]s are I/O objects, which know how to
retrieve rows. This allows to read rows progressively, allowing for better
efficiency.
-There are three methods of retrieving rows, which resemble
-[@https://www.python.org/dev/peps/pep-0249/#fetchone Python's DB v2.0 cursor API]:
+There are three methods of retrieving rows:
-* [refmem resultset fetch_one] and [refmem resultset async_fetch_one]:
+* [refmem resultset read_one] and [refmem resultset async_read_one]:
retrieves a single row.
-* [refmem resultset fetch_many] and [refmem resultset async_fetch_many]:
+* [refmem resultset read_many] and [refmem resultset async_read_many]:
retrieves an array of rows, up to a certain count.
-* [refmem resultset fetch_all] and [refmem resultset async_fetch_all]:
+* [refmem resultset read_all] and [refmem resultset async_read_all]:
retrieves all the remaining rows.
[heading Fetching a single row]
-The [refmem resultset fetch_one] family retrieves the next row.
-It returns a `const` [reflink row]\* pointer. When there are no more
-rows to be read, it will return `nullptr`. You can use it as follows:
+The [refmem resultset read_one] family retrieve the next row.
+The functions get passed a [reflink row] lvalue reference, which
+will get populated by the next read row. The function returns a
+`bool` indicating whether a row was read or not. You can use it as follows:
``
tcp_resultset result = /* obtain a resultset, e.g. via connection::query */
-while (const row* row_obj = result.fetch_one())
+row row_obj;
+while (result.read_one(row_obj))
{
- // Do stuff with *row_obj
+ // Do stuff with row_obj
}
``
-The returned [reflink row] is owned by the [reflink resultset].
-Note also that, if any of the fields are strings, the memory
-pointed to by the values will also be owned by the [reflink resultset].
-This object will remain valid until you call [*any] of the fetch
-functions again, or you destroy the resultset.
-
-[warning
- Use [refmem resultset fetch_one] with caution. If you
- need the values to outlive the [reflink resultset],
- use the other fetch methods.
-]
+Note that if any of the fields are strings, the memory
+pointed to by the values will be owned by the [reflink row] object.
+Thus, these values will be valid as long as the [reflink row] object
+is kept alive and valid.
The examples on async queries with [link mysql.examples.query_async_futures
futures], [link mysql.examples.query_async_coroutines Boost.Coroutine coroutines]
and [link mysql.examples.query_async_coroutinescpp20 C++20 coroutines]
-make use of [refmem resultset async_fetch_one].
+make use of [refmem resultset async_read_one].
[heading Fetching multiple rows]
-The [refmem resultset fetch_many] family retrieve many
+The [refmem resultset read_many] family retrieve many
rows at a single time. When you call them, you pass in
the maximum number of rows you want to read. For example:
``
tcp_resultset result = /* obtain a resultset, e.g. via connection::query */
-std::vector rows = result.fetch_many(5); // at most 5 rows
+std::vector rows = result.read_many(5); // at most 5 rows
``
-Note that, contrary to what happened in [refmem resultset fetch_one],
-these functions return [reflink owning_row] objects.
-This means that, if any of the fields are strings, the [reflink value]
-objects will point into memory owned by the [reflink owning_row]
-object. With [refmem resultset fetch_one], this memory was owned
-by the [reflink resultset]. This also means that you can call
-any other fetch methods without the row objects becoming invalid.
-[reflink owning_row] objects may outlive their [reflink resultset]
-object without incurring in undefined behavior.
-
-The [refmem resultset fetch_all] functions work similarly to
-[refmem resultset fetch_many], except that they retrieve all the
+The [refmem resultset read_all] functions work similarly to
+[refmem resultset read_many], except that they retrieve all the
rows in the resultset.
[endsect]
@@ -123,10 +105,10 @@ read a row, but there are not any more rows available. For example,
in a resultset with 4 rows, any of the following actions will
complete the resultset:
-* Calling [refmem resultset fetch_one] 5 times.
-* Calling [refmem resultset fetch_many] with a count of 5 or greater
+* Calling [refmem resultset read_one] 5 times.
+* Calling [refmem resultset read_many] with a count of 5 or greater
(or several times, with a total count of 5 or greater).
-* Calling [refmem resultset fetch_all].
+* Calling [refmem resultset read_all].
After a [reflink resultset] is complete, some extra information about
the query becomes available, like [refmem resultset warning_count]
@@ -138,7 +120,7 @@ A resultset may also be [*empty], which means it has no row
to be retrieved. Non `SELECT` SQL statements (e.g. `UPDATE`
and `INSERT`) generate empty resultsets. Empty resultsets
are complete from the beginning: you don't need to
-call [refmem resultset fetch_one] to make them complete.
+call [refmem resultset read_one] to make them complete.
[warning
Because of how the MySQL protocol works, once you obtain
@@ -187,7 +169,7 @@ how the [reflink resultset] was generated:
(the protocol provides no option to avoid this).
However you can choose to start processing them
as soon as the first one arrives by using
- [refmem resultset fetch_one] or [refmem resultset fetch_many].
+ [refmem resultset read_one] or [refmem resultset read_many].
* If it was generated using [refmem prepared_statement execute]
or [refmem prepared_statement async_execute], then you are
using the [*binary protocol]. The current implementation
@@ -195,8 +177,8 @@ how the [reflink resultset] was generated:
making it similar to the case above. However, the
protocol does allow sending rows progressively.
Future implementations may take advantage of this
- fact when using [refmem resultset fetch_one] or
- [refmem resultset fetch_many].
+ fact when using [refmem resultset read_one] or
+ [refmem resultset read_many].
[endsect]
diff --git a/doc/qbk/tutorial.qbk b/doc/qbk/tutorial.qbk
index fe692209..f5c2ad2d 100644
--- a/doc/qbk/tutorial.qbk
+++ b/doc/qbk/tutorial.qbk
@@ -73,15 +73,15 @@ A [reflink resultset] is an object that represents the result of a query,
in tabular format. Resultsets are not containers, but I/O objects:
they do not contain by themselves the entire result of the query, but allow
the user to fetch it using several methods. We will use
-[reflink2 resultset.fetch_all tcp_resultset::fetch_all], which just
+[reflink2 resultset.read_all tcp_resultset::read_all], which just
reads all the rows in the resultset and places them in a `std::vector`
-of [reflink owning_row] objects:
+of [reflink row] objects:
[tutorial_fetch]
Rows are essentially arrays of [reflink value]s . A [reflink value]
is a union-like class of all types allowed in MySQL. You can access the row's
-contents using [refmem owning_row values].
+contents using [refmem row values].
[tutorial_values]
diff --git a/doc/qbk/values.qbk b/doc/qbk/values.qbk
index fba2fe46..df4ede1c 100644
--- a/doc/qbk/values.qbk
+++ b/doc/qbk/values.qbk
@@ -132,11 +132,10 @@ Instead, prefer the following:
]
In __Self__, the memory pointed to by string [reflink value]s
-is owned by either [reflink resultset] or [reflink owning_row]
-objects. This avoids unnecessary copies and makes the [reflink value]
+is owned by the [reflink row] objects the values belong to.
+This avoids unnecessary copies and makes the [reflink value]
class lightweight and cheap to copy. In exchange, you must pay attention
-to the lifetime of the [reflink resultset] or [reflink owning_row]
-object you used to obtain the [reflink value] to not incur in
+to the lifetime of the [reflink row] object you used to obtain the [reflink value] to not incur in
undefined behavior. For more information on resultset mechanics,
see [link mysql.resultsets this section].
@@ -147,7 +146,7 @@ see [link mysql.resultsets this section].
Values may be streamed and compared for equality. If you need
to `visit()` a [reflink value], use [refmem value to_variant]
and use `visit()` on the returned variant. Additionally,
-[reflink row] and [reflink owning_row] objects may also
+[reflink row] objects may also
be streamed and compared for equality.
[endsect]
diff --git a/doc/xsl/config.xsl b/doc/xsl/config.xsl
index 338e95d7..33f0db1c 100644
--- a/doc/xsl/config.xsl
+++ b/doc/xsl/config.xsl
@@ -11,4 +11,5 @@
+
diff --git a/example/default_completion_tokens.cpp b/example/default_completion_tokens.cpp
index e7bfb9f7..672c4385 100644
--- a/example/default_completion_tokens.cpp
+++ b/example/default_completion_tokens.cpp
@@ -7,6 +7,7 @@
//[example_default_completion_tokens]
#include "boost/mysql/mysql.hpp"
+#include "boost/mysql/row.hpp"
#include
#include
#include
@@ -111,15 +112,15 @@ boost::asio::awaitable start_query(
auto result = co_await conn.async_query(sql);
/**
- * Get all rows in the resultset. We will employ resultset::async_fetch_one(),
- * which returns a single row at every call. The returned row is a pointer
- * to memory owned by the resultset, and is re-used for each row. Thus, returned
- * rows remain valid until the next call to async_fetch_one(). When no more
- * rows are available, async_fetch_one returns nullptr.
+ * Get all rows in the resultset. We will employ resultset::async_read_one(),
+ * which reads a single row at every call. The row is read in-place, preventing
+ * unnecessary copies. resultset::async_read_one() returns true if a row has been
+ * read, false if no more rows are available or an error occurred.
*/
- while (const boost::mysql::row* row = co_await result.async_fetch_one())
+ boost::mysql::row row;
+ while (co_await result.async_read_one(row))
{
- print_employee(*row);
+ print_employee(row);
}
// Notify the MySQL server we want to quit, then close the underlying connection.
diff --git a/example/prepared_statements.cpp b/example/prepared_statements.cpp
index 7d7f927f..21cdd952 100644
--- a/example/prepared_statements.cpp
+++ b/example/prepared_statements.cpp
@@ -87,7 +87,7 @@ void main_impl(int argc, char** argv)
*/
//[prepared_statements_execute
boost::mysql::tcp_resultset result = salary_getter.execute(boost::mysql::make_values("Efficient"));
- std::vector salaries = result.fetch_all(); // Get all the results
+ std::vector salaries = result.read_all(); // Get all the results
//]
ASSERT(salaries.size() == 1);
double salary = salaries[0].values().at(0).get(); // First row, first column
@@ -109,7 +109,7 @@ void main_impl(int argc, char** argv)
* connection::prepare_statement() again.
*/
result = salary_getter.execute(boost::mysql::make_values("Efficient"));
- salaries = result.fetch_all();
+ salaries = result.read_all();
ASSERT(salaries.size() == 1);
salary = salaries[0].values().at(0).get();
ASSERT(salary == 35000); // Our update took place, and the dev got his pay rise
diff --git a/example/query_async_callbacks.cpp b/example/query_async_callbacks.cpp
index b4a40b34..8c59bdef 100644
--- a/example/query_async_callbacks.cpp
+++ b/example/query_async_callbacks.cpp
@@ -17,7 +17,7 @@
using boost::mysql::error_code;
using boost::mysql::error_info;
using boost::mysql::tcp_resultset;
-using boost::mysql::owning_row;
+using boost::mysql::row;
#define ASSERT(expr) \
if (!(expr)) \
@@ -78,7 +78,7 @@ public:
connection.async_query(sql, additional_info, [this](error_code err, tcp_resultset&& result) {
die_on_error(err, additional_info);
resultset = std::move(result);
- resultset.async_fetch_all(additional_info, [this](error_code err, const std::vector& rows) {
+ resultset.async_read_all(additional_info, [this](error_code err, const std::vector& rows) {
die_on_error(err, additional_info);
for (const auto& employee: rows)
{
@@ -106,7 +106,7 @@ public:
connection.async_query(sql, additional_info, [this](error_code err, tcp_resultset&& result) {
die_on_error(err, additional_info);
resultset = std::move(result);
- resultset.async_fetch_all(additional_info, [this](error_code err, const std::vector& rows) {
+ resultset.async_read_all(additional_info, [this](error_code err, const std::vector& rows) {
die_on_error(err, additional_info);
ASSERT(rows.size() == 1);
auto salary = rows[0].values()[0].get();
diff --git a/example/query_async_coroutines.cpp b/example/query_async_coroutines.cpp
index fc1660e2..eb519dcd 100644
--- a/example/query_async_coroutines.cpp
+++ b/example/query_async_coroutines.cpp
@@ -8,6 +8,7 @@
//[example_query_async_coroutines
#include "boost/mysql/mysql.hpp"
+#include "boost/mysql/row.hpp"
#include
#include
#include
@@ -90,18 +91,16 @@ void main_impl(int argc, char** argv)
check_error(ec, additional_info);
/**
- * Get all rows in the resultset. We will employ resultset::async_fetch_one(),
- * which returns a single row at every call. The returned row is a pointer
- * to memory owned by the resultset, and is re-used for each row. Thus, returned
- * rows remain valid until the next call to async_fetch_one(). When no more
- * rows are available, async_fetch_one returns nullptr.
+ * Get all rows in the resultset. We will employ resultset::async_read_one(),
+ * which reads a single row at every call. The row is read in-place, preventing
+ * unnecessary copies. resultset::async_read_one() returns true if a row has been
+ * read, false if no more rows are available or an error occurred.
*/
- while (true)
+ boost::mysql::row row;
+ while (result.async_read_one(row, additional_info, yield[ec]))
{
- const boost::mysql::row* row = result.async_fetch_one(additional_info, yield[ec]);
check_error(ec, additional_info);
- if (!row) break; // No more rows available
- print_employee(*row);
+ print_employee(row);
}
// Notify the MySQL server we want to quit, then close the underlying connection.
diff --git a/example/query_async_coroutinescpp20.cpp b/example/query_async_coroutinescpp20.cpp
index 7673d0b6..5a61edcb 100644
--- a/example/query_async_coroutinescpp20.cpp
+++ b/example/query_async_coroutinescpp20.cpp
@@ -8,6 +8,7 @@
//[example_query_async_coroutinescpp20
#include "boost/mysql/mysql.hpp"
+#include "boost/mysql/row.hpp"
#include
#include
#include
@@ -101,16 +102,15 @@ boost::asio::awaitable start_query(
auto result = co_await conn.async_query(sql, boost::asio::use_awaitable);
/**
- * Get all rows in the resultset. We will employ resultset::async_fetch_one(),
- * which returns a single row at every call. The returned row is a pointer
- * to memory owned by the resultset, and is re-used for each row. Thus, returned
- * rows remain valid until the next call to async_fetch_one(). When no more
- * rows are available, async_fetch_one returns nullptr.
- */
- while (const boost::mysql::row* row =
- co_await result.async_fetch_one(boost::asio::use_awaitable))
+ * Get all rows in the resultset. We will employ resultset::async_read_one(),
+ * which reads a single row at every call. The row is read in-place, preventing
+ * unnecessary copies. resultset::async_read_one() returns true if a row has been
+ * read, false if no more rows are available or an error occurred.
+ */
+ boost::mysql::row row;
+ while (co_await result.async_read_one(row, boost::asio::use_awaitable))
{
- print_employee(*row);
+ print_employee(row);
}
// Notify the MySQL server we want to quit, then close the underlying connection.
diff --git a/example/query_async_futures.cpp b/example/query_async_futures.cpp
index 6113bf7b..101379ca 100644
--- a/example/query_async_futures.cpp
+++ b/example/query_async_futures.cpp
@@ -8,6 +8,7 @@
//[example_query_async_futures
#include "boost/mysql/mysql.hpp"
+#include "boost/mysql/row.hpp"
#include
#include
#include
@@ -90,15 +91,15 @@ void main_impl(int argc, char** argv)
boost::mysql::tcp_resultset result = resultset_fut.get();
/**
- * Get all rows in the resultset. We will employ resultset::async_fetch_one(),
- * which returns a single row at every call. The returned row is a pointer
- * to memory owned by the resultset, and is re-used for each row. Thus, returned
- * rows remain valid until the next call to async_fetch_one(). When no more
- * rows are available, async_fetch_one returns nullptr.
- */
- while (const boost::mysql::row* current_row = result.async_fetch_one(use_future).get())
+ * Get all rows in the resultset. We will employ resultset::async_read_one(),
+ * which reads a single row at every call. The row is read in-place, preventing
+ * unnecessary copies. resultset::async_read_one() returns true if a row has been
+ * read, false if no more rows are available or an error occurred.
+ */
+ boost::mysql::row row;
+ while (result.async_read_one(row, use_future).get())
{
- print_employee(*current_row);
+ print_employee(row);
}
// Notify the MySQL server we want to quit, then close the underlying connection.
diff --git a/example/query_sync.cpp b/example/query_sync.cpp
index 40be788f..f5571f35 100644
--- a/example/query_sync.cpp
+++ b/example/query_sync.cpp
@@ -83,8 +83,8 @@ void main_impl(int argc, char** argv)
*
* Resultset objects represent the result of a query, in tabular format.
* They hold metadata describing the fields the resultset holds (in this case, first_name,
- * last_name and salary). To get the actual data, use fetch_one, fetch_many or fetch_all.
- * We will use fetch_all, which returns all the received rows as a std::vector.
+ * last_name and salary). To get the actual data, use read_one, read_many or read_all.
+ * We will use read_all, which returns all the received rows as a std::vector.
*
* We will get all employees working for 'High Growth Startup'.
*/
@@ -92,7 +92,7 @@ void main_impl(int argc, char** argv)
boost::mysql::tcp_resultset result = conn.query(sql);
// Get all the rows in the resultset
- std::vector employees = result.fetch_all();
+ std::vector employees = result.read_all();
for (const auto& employee: employees)
{
print_employee(employee);
@@ -106,7 +106,7 @@ void main_impl(int argc, char** argv)
// Check we have updated our poor intern salary
result = conn.query("SELECT salary FROM employee WHERE first_name = 'Underpaid'");
- auto rows = result.fetch_all();
+ auto rows = result.read_all();
ASSERT(rows.size() == 1);
double salary = rows[0].values()[0].get();
ASSERT(salary == 10000);
diff --git a/example/tutorial.cpp b/example/tutorial.cpp
index 7b9b441b..90a92f71 100644
--- a/example/tutorial.cpp
+++ b/example/tutorial.cpp
@@ -52,7 +52,7 @@ void main_impl(int argc, char** argv)
//]
//[tutorial_fetch
- std::vector employees = result.fetch_all();
+ std::vector employees = result.read_all();
//]
//[tutorial_values
diff --git a/example/unix_socket.cpp b/example/unix_socket.cpp
index 85cc371e..93f1fef3 100644
--- a/example/unix_socket.cpp
+++ b/example/unix_socket.cpp
@@ -74,7 +74,7 @@ void main_impl(int argc, char** argv)
boost::mysql::unix_resultset result = conn.query(sql);
// Get all the rows in the resultset
- std::vector employees = result.fetch_all();
+ std::vector employees = result.read_all();
for (const auto& employee: employees)
{
print_employee(employee);
@@ -88,7 +88,7 @@ void main_impl(int argc, char** argv)
// Check we have updated our poor intern salary
result = conn.query("SELECT salary FROM employee WHERE first_name = 'Underpaid'");
- auto rows = result.fetch_all();
+ auto rows = result.read_all();
ASSERT(rows.size() == 1);
double salary = rows[0].values()[0].get();
ASSERT(salary == 10000);
diff --git a/include/boost/mysql/detail/network_algorithms/impl/read_row.hpp b/include/boost/mysql/detail/network_algorithms/impl/read_row.hpp
index 4236bd31..924561d6 100644
--- a/include/boost/mysql/detail/network_algorithms/impl/read_row.hpp
+++ b/include/boost/mysql/detail/network_algorithms/impl/read_row.hpp
@@ -18,8 +18,8 @@ inline read_row_result process_read_message(
deserialize_row_fn deserializer,
capabilities current_capabilities,
const std::vector& meta,
- const bytestring& buffer,
- std::vector& output_values,
+ row& output,
+ bytestring& ok_packet_buffer,
ok_packet& output_ok_packet,
error_code& err,
error_info& info
@@ -29,17 +29,19 @@ inline read_row_result process_read_message(
// Message type: row, error or eof?
std::uint8_t msg_type = 0;
- deserialization_context ctx (boost::asio::buffer(buffer), current_capabilities);
+ deserialization_context ctx (boost::asio::buffer(output.buffer()), current_capabilities);
err = make_error_code(deserialize(ctx, msg_type));
if (err)
return read_row_result::error;
if (msg_type == eof_packet_header)
{
- // end of resultset
+ // end of resultset => we read the packet against the row, but it is the ok_packet, instead
err = deserialize_message(ctx, output_ok_packet);
- return err ? read_row_result::error : read_row_result::eof;
if (err)
return read_row_result::error;
+ std::swap(output.buffer(), ok_packet_buffer);
+ output.buffer().clear();
+ output.values().clear();
return read_row_result::eof;
}
else if (msg_type == error_packet_header)
@@ -52,7 +54,7 @@ inline read_row_result process_read_message(
{
// An actual row
ctx.rewind(1); // keep the 'message type' byte, as it is part of the actual message
- err = deserializer(ctx, meta, output_values);
+ err = deserializer(ctx, meta, output.values());
if (err)
return read_row_result::error;
return read_row_result::row;
@@ -66,8 +68,8 @@ struct read_row_op : boost::asio::coroutine
error_info& output_info_;
deserialize_row_fn deserializer_;
const std::vector& meta_;
- bytestring& buffer_;
- std::vector& output_values_;
+ row& output_;
+ bytestring& ok_packet_buffer_;
ok_packet& output_ok_packet_;
read_row_op(
@@ -75,16 +77,16 @@ struct read_row_op : boost::asio::coroutine
error_info& output_info,
deserialize_row_fn deserializer,
const std::vector& meta,
- bytestring& buffer,
- std::vector& output_values,
+ row& output,
+ bytestring& ok_packet_buffer,
ok_packet& output_ok_packet
) :
chan_(chan),
output_info_(output_info),
deserializer_(deserializer),
meta_(meta),
- buffer_(buffer),
- output_values_(output_values),
+ output_(output),
+ ok_packet_buffer_(ok_packet_buffer),
output_ok_packet_(output_ok_packet)
{
}
@@ -108,15 +110,15 @@ struct read_row_op : boost::asio::coroutine
BOOST_ASIO_CORO_REENTER(*this)
{
// Read the message
- BOOST_ASIO_CORO_YIELD chan_.async_read(buffer_, std::move(self));
+ BOOST_ASIO_CORO_YIELD chan_.async_read(output_.buffer(), std::move(self));
// Process it
result = process_read_message(
deserializer_,
chan_.current_capabilities(),
meta_,
- buffer_,
- output_values_,
+ output_,
+ ok_packet_buffer_,
output_ok_packet_,
err,
output_info_
@@ -136,15 +138,15 @@ boost::mysql::detail::read_row_result boost::mysql::detail::read_row(
deserialize_row_fn deserializer,
channel& channel,
const std::vector& meta,
- bytestring& buffer,
- std::vector& output_values,
+ row& output,
+ bytestring& ok_packet_buffer,
ok_packet& output_ok_packet,
error_code& err,
error_info& info
)
{
// Read a packet
- channel.read(buffer, err);
+ channel.read(output.buffer(), err);
if (err)
return read_row_result::error;
@@ -152,8 +154,8 @@ boost::mysql::detail::read_row_result boost::mysql::detail::read_row(
deserializer,
channel.current_capabilities(),
meta,
- buffer,
- output_values,
+ output,
+ ok_packet_buffer,
output_ok_packet,
err,
info
@@ -169,8 +171,8 @@ boost::mysql::detail::async_read_row(
deserialize_row_fn deserializer,
channel& chan,
const std::vector& meta,
- bytestring& buffer,
- std::vector& output_values,
+ row& output,
+ bytestring& ok_packet_buffer,
ok_packet& output_ok_packet,
CompletionToken&& token,
error_info& output_info
@@ -182,8 +184,8 @@ boost::mysql::detail::async_read_row(
output_info,
deserializer,
meta,
- buffer,
- output_values,
+ output,
+ ok_packet_buffer,
output_ok_packet
),
token,
diff --git a/include/boost/mysql/detail/network_algorithms/read_row.hpp b/include/boost/mysql/detail/network_algorithms/read_row.hpp
index 1edb94f9..e22441a1 100644
--- a/include/boost/mysql/detail/network_algorithms/read_row.hpp
+++ b/include/boost/mysql/detail/network_algorithms/read_row.hpp
@@ -10,6 +10,7 @@
#include "boost/mysql/detail/network_algorithms/common.hpp"
#include "boost/mysql/metadata.hpp"
+#include "boost/mysql/row.hpp"
#include
#include
@@ -29,8 +30,8 @@ read_row_result read_row(
deserialize_row_fn deserializer,
channel& channel,
const std::vector& meta,
- bytestring& buffer,
- std::vector& output_values,
+ row& output,
+ bytestring& ok_packet_buffer,
ok_packet& output_ok_packet,
error_code& err,
error_info& info
@@ -42,9 +43,9 @@ async_read_row(
deserialize_row_fn deserializer,
channel& channel,
const std::vector& meta,
- bytestring& buffer,
- std::vector& output_values,
- ok_packet& output_ok_packet,
+ row& output,
+ bytestring& ok_packet_buffer,
+ ok_packet& output_ok_packet,
CompletionToken&& token,
error_info& output_info
);
diff --git a/include/boost/mysql/impl/resultset.hpp b/include/boost/mysql/impl/resultset.hpp
index 1227974c..1dab1926 100644
--- a/include/boost/mysql/impl/resultset.hpp
+++ b/include/boost/mysql/impl/resultset.hpp
@@ -13,9 +13,11 @@
#include
#include
#include
+#include
template
-const boost::mysql::row* boost::mysql::resultset::fetch_one(
+bool boost::mysql::resultset::read_one(
+ row& output,
error_code& err,
error_info& info
)
@@ -26,33 +28,36 @@ const boost::mysql::row* boost::mysql::resultset::fetch_one(
if (complete())
{
- return nullptr;
+ output.clear();
+ return false;
}
auto result = detail::read_row(
deserializer_,
*channel_,
meta_.fields(),
- buffer_,
- current_row_.values(),
+ output,
+ ok_packet_buffer_,
ok_packet_,
err,
info
);
eof_received_ = result == detail::read_row_result::eof;
- return result == detail::read_row_result::row ? ¤t_row_ : nullptr;
+ return result == detail::read_row_result::row;
}
template
-const boost::mysql::row* boost::mysql::resultset::fetch_one()
+bool boost::mysql::resultset::read_one(
+ row& output
+)
{
detail::error_block blk;
- const row* res = fetch_one(blk.err, blk.info);
+ bool res = read_one(output, blk.err, blk.info);
blk.check();
return res;
}
template
-std::vector boost::mysql::resultset::fetch_many(
+std::vector boost::mysql::resultset::read_many(
std::size_t count,
error_code& err,
error_info& info
@@ -62,78 +67,65 @@ std::vector boost::mysql::resultset::fetch_man
detail::clear_errors(err, info);
- std::vector res;
+ std::vector res;
- if (!complete()) // support calling fetch on already exhausted resultset
- {
- for (std::size_t i = 0; i < count; ++i)
- {
- detail::bytestring buff;
- std::vector values;
-
- auto result = detail::read_row(
- deserializer_,
- *channel_,
- meta_.fields(),
- buff,
- values,
- ok_packet_,
- err,
- info
- );
- eof_received_ = result == detail::read_row_result::eof;
- if (result == detail::read_row_result::row)
- {
- res.emplace_back(std::move(values), std::move(buff));
- }
- else
- {
- break;
- }
- }
- }
+ for (std::size_t i = 0; i < count; ++i)
+ {
+ row r;
+ if (this->read_one(r, err, info))
+ {
+ res.push_back(std::move(r));
+ }
+ else
+ {
+ break;
+ }
+ }
return res;
}
template
-std::vector boost::mysql::resultset::fetch_many(
+std::vector boost::mysql::resultset::read_many(
std::size_t count
)
{
detail::error_block blk;
- auto res = fetch_many(count, blk.err, blk.info);
+ auto res = read_many(count, blk.err, blk.info);
blk.check();
return res;
}
template
-std::vector boost::mysql::resultset::fetch_all(
+std::vector boost::mysql::resultset::read_all(
error_code& err,
error_info& info
)
{
- return fetch_many(std::numeric_limits::max(), err, info);
+ return read_many(std::numeric_limits::max(), err, info);
}
template
-std::vector boost::mysql::resultset::fetch_all()
+std::vector boost::mysql::resultset::read_all()
{
- return fetch_many(std::numeric_limits::max());
+ return read_many(std::numeric_limits::max());
}
template
-struct boost::mysql::resultset::fetch_one_op
+struct boost::mysql::resultset::read_one_op
: boost::asio::coroutine
{
resultset& resultset_;
+ row& output_;
error_info& output_info_;
- fetch_one_op(
+ read_one_op(
resultset& obj,
+ row& output,
error_info& output_info
) :
resultset_(obj),
+ output_(output),
output_info_(output_info)
{
}
@@ -151,15 +143,16 @@ struct boost::mysql::resultset::fetch_one_op
{
// ensure return as if by post
BOOST_ASIO_CORO_YIELD boost::asio::post(std::move(self));
- self.complete(error_code(), nullptr);
+ output_.clear();
+ self.complete(error_code(), false);
BOOST_ASIO_CORO_YIELD break;
}
BOOST_ASIO_CORO_YIELD detail::async_read_row(
resultset_.deserializer_,
*resultset_.channel_,
resultset_.meta_.fields(),
- resultset_.buffer_,
- resultset_.current_row_.values(),
+ output_,
+ resultset_.ok_packet_buffer_,
resultset_.ok_packet_,
std::move(self),
output_info_
@@ -167,7 +160,7 @@ struct boost::mysql::resultset::fetch_one_op
resultset_.eof_received_ = result == detail::read_row_result::eof;
self.complete(
err,
- result == detail::read_row_result::row ? &resultset_.current_row_ : nullptr
+ result == detail::read_row_result::row
);
}
}
@@ -175,20 +168,21 @@ struct boost::mysql::resultset::fetch_one_op
template
template
+ void(boost::mysql::error_code, bool)) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
- void(boost::mysql::error_code, const boost::mysql::row*)
+ void(boost::mysql::error_code, bool)
)
-boost::mysql::resultset::async_fetch_one(
+boost::mysql::resultset::async_read_one(
+ row& output,
error_info& output_info,
CompletionToken&& token
)
{
assert(valid());
output_info.clear();
- return boost::asio::async_compose(
- fetch_one_op(*this, output_info),
+ return boost::asio::async_compose(
+ read_one_op(*this, output, output_info),
token,
*this
);
@@ -197,15 +191,14 @@ boost::mysql::resultset::async_fetch_one(
template
-struct boost::mysql::resultset::fetch_many_op
+struct boost::mysql::resultset::read_many_op
: boost::asio::coroutine
{
struct impl_struct
{
resultset& parent_resultset;
- std::vector rows;
- detail::bytestring buffer;
- std::vector values;
+ std::vector rows;
+ row current_row;
std::size_t remaining;
error_info& output_info;
bool cont {false};
@@ -219,16 +212,15 @@ struct boost::mysql::resultset::fetch_many_op
void row_received()
{
- rows.emplace_back(std::move(values), std::move(buffer));
- values = std::vector();
- buffer = detail::bytestring();
+ rows.push_back(std::move(current_row));
+ current_row = row();
--remaining;
}
};
std::shared_ptr impl_;
- fetch_many_op(
+ read_many_op(
resultset& obj,
error_info& output_info,
std::size_t count
@@ -258,7 +250,7 @@ struct boost::mysql::resultset::fetch_many_op
void operator()(
Self& self,
error_code err = {},
- detail::read_row_result result=detail::read_row_result::error
+ detail::read_row_result result = detail::read_row_result::error
)
{
impl_struct& impl = *impl_;
@@ -271,8 +263,8 @@ struct boost::mysql::resultset::fetch_many_op
impl.parent_resultset.deserializer_,
*impl.parent_resultset.channel_,
impl.parent_resultset.meta_.fields(),
- impl.buffer,
- impl.values,
+ impl.current_row,
+ impl.parent_resultset.ok_packet_buffer_,
impl.parent_resultset.ok_packet_,
std::move(self),
impl.output_info
@@ -307,12 +299,12 @@ struct boost::mysql::resultset::fetch_many_op
template
template )) CompletionToken>
+ void(boost::mysql::error_code, std::vector)) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
- void(boost::mysql::error_code, std::vector)
+ void(boost::mysql::error_code, std::vector)
)
-boost::mysql::resultset::async_fetch_many(
+boost::mysql::resultset::async_read_many(
std::size_t count,
error_info& output_info,
CompletionToken&& token
@@ -322,9 +314,9 @@ boost::mysql::resultset::async_fetch_many(
output_info.clear();
return boost::asio::async_compose<
CompletionToken,
- void(error_code, std::vector)
+ void(error_code, std::vector)
>(
- fetch_many_op(*this, output_info, count),
+ read_many_op(*this, output_info, count),
token,
*this
);
@@ -332,17 +324,17 @@ boost::mysql::resultset::async_fetch_many(
template
template )) CompletionToken>
+ void(boost::mysql::error_code, std::vector)) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
- void(boost::mysql::error_code, std::vector)
+ void(boost::mysql::error_code, std::vector)
)
-boost::mysql::resultset::async_fetch_all(
+boost::mysql::resultset::async_read_all(
error_info& info,
CompletionToken&& token
)
{
- return async_fetch_many(
+ return async_read_many(
std::numeric_limits::max(),
info,
std::forward(token)
diff --git a/include/boost/mysql/resultset.hpp b/include/boost/mysql/resultset.hpp
index ecc2f676..4136fbd0 100644
--- a/include/boost/mysql/resultset.hpp
+++ b/include/boost/mysql/resultset.hpp
@@ -42,16 +42,15 @@ class resultset
detail::deserialize_row_fn deserializer_ {};
detail::channel* channel_;
detail::resultset_metadata meta_;
- row current_row_;
- detail::bytestring buffer_;
+ detail::bytestring ok_packet_buffer_;
detail::ok_packet ok_packet_;
bool eof_received_ {false};
error_info& shared_info() noexcept { assert(channel_); return channel_->shared_info(); }
- struct fetch_one_op;
- struct fetch_many_op;
- struct fetch_many_op_impl;
+ struct read_one_op;
+ struct read_many_op;
+ struct read_many_op_impl;
public:
/// \brief Default constructor.
@@ -65,7 +64,7 @@ class resultset
deserializer_(deserializer), channel_(&channel), meta_(std::move(meta)) {};
resultset(detail::channel& channel, detail::bytestring&& buffer,
const detail::ok_packet& ok_pack):
- channel_(&channel), buffer_(std::move(buffer)), ok_packet_(ok_pack), eof_received_(true) {};
+ channel_(&channel), ok_packet_buffer_(std::move(buffer)), ok_packet_(ok_pack), eof_received_(true) {};
#endif
/// The executor type associated to the object.
@@ -82,184 +81,167 @@ class resultset
/**
* \brief Fetches a single row (sync with error code version).
- * \details The returned object will be `nullptr` if there are no more rows
- * to read. Calling this function on a complete resultset returns `nullptr`.
- *
- * The returned [reflink row] points into memory owned by the resultset. Destroying
- * or moving the resultset object invalidates it. Calling
- * any of the fetch methods again also invalidates it.
+ * \details Returns `true` if a row was read successfully, `false` if
+ * there was an error or there were no more rows to read. Calling
+ * this function on a complete resultset always returns `false`.
+ *
+ * If the operation succeeds and returns `true`, the new row will be
+ * read against `output`, possibly reusing its memory. If the operation
+ * succeeds but returns `false`, `output` will be set to the empty row
+ * (as if [refmem row clear] was called). If the operation fails,
+ * `output` is left in a valid but undetrmined state.
*/
- const row* fetch_one(error_code& err, error_info& info);
+ bool read_one(row& output, error_code& err, error_info& info);
/**
* \brief Fetches a single row (sync with exceptions version).
- * \details The returned object will be `nullptr` if there are no more rows
- * to read. Calling this function on a complete resultset returns `nullptr`.
- *
- * The returned [reflink row] points into memory owned by the resultset. Destroying
- * or moving the resultset object invalidates it. Calling
- * any of the fetch methods again also invalidates it.
+ * \details Returns `true` if a row was read successfully, `false` if
+ * there was an error or there were no more rows to read. Calling
+ * this function on a complete resultset always returns `false`.
+ *
+ * If the operation succeeds and returns `true`, the new row will be
+ * read against `output`, possibly reusing its memory. If the operation
+ * succeeds but returns `false`, `output` will be set to the empty row
+ * (as if [refmem row clear] was called). If the operation fails,
+ * `output` is left in a valid but undetrmined state.
*/
- const row* fetch_one();
+ bool read_one(row& output);
/**
* \brief Fetches a single row (async without [reflink error_info] version).
- * \details The pointer passed to the completion handler
- * will be `nullptr` if there are no more rows
- * to read. Calling this function on a complete resultset returns `nullptr`.
- *
- * The [reflink row] passed to the completion handler
- * points into memory owned by the resultset. Destroying
- * or moving the resultset object invalidates it. Calling
- * any of the fetch methods again also invalidates it.
+ * \details Completes with `true` if a row was read successfully, and with `false` if
+ * there was an error or there were no more rows to read. Calling
+ * this function on a complete resultset always returns `false`.
+ *
+ * If the operation succeeds and completes with `true`, the new row will be
+ * read against `output`, possibly reusing its memory. If the operation
+ * succeeds but completes with `false`, `output` will be set to the empty row
+ * (as if [refmem row clear] was called). If the operation fails,
+ * `output` is left in a valid but undetrmined state.
*
* The handler signature for this operation is
- * `void(boost::mysql::error_code, const boost::mysql::row*)`.
+ * `void(boost::mysql::error_code, bool)`.
*/
template <
- BOOST_ASIO_COMPLETION_TOKEN_FOR(void(error_code, const row*))
+ BOOST_ASIO_COMPLETION_TOKEN_FOR(void(error_code, bool))
CompletionToken
BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)
>
- BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, const row*))
- async_fetch_one(CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type))
+ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, bool))
+ async_read_one(row& output, CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type))
{
- return async_fetch_one(shared_info(), std::forward(token));
+ return async_read_one(output, shared_info(), std::forward(token));
}
/**
* \brief Fetches a single row (async with [reflink error_info] version).
- * \details The pointer passed to the completion handler
- * will be `nullptr` if there are no more rows
- * to read. Calling this function on a complete resultset returns `nullptr`.
- *
- * The [reflink row] passed to the completion handler
- * points into memory owned by the resultset. Destroying
- * or moving the resultset object invalidates it. Calling
- * any of the fetch methods again also invalidates it.
+ * \details Completes with `true` if a row was read successfully, and with `false` if
+ * there was an error or there were no more rows to read. Calling
+ * this function on a complete resultset always returns `false`.
+ *
+ * If the operation succeeds and completes with `true`, the new row will be
+ * read against `output`, possibly reusing its memory. If the operation
+ * succeeds but completes with `false`, `output` will be set to the empty row
+ * (as if [refmem row clear] was called). If the operation fails,
+ * `output` is left in a valid but undetrmined state.
*
* The handler signature for this operation is
- * `void(boost::mysql::error_code, const boost::mysql::row*)`.
+ * `void(boost::mysql::error_code, bool)`.
*/
template <
- BOOST_ASIO_COMPLETION_TOKEN_FOR(void(error_code, const row*))
+ BOOST_ASIO_COMPLETION_TOKEN_FOR(void(error_code, bool))
CompletionToken
BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)
>
- BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, const row*))
- async_fetch_one(
+ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, bool))
+ async_read_one(
+ row& output,
error_info& output_info,
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
);
- /**
- * \brief Fetches several rows, up to a maximum
- * (sync with error code version).
- * \details The returned rows may outlive the resultset.
- * Subsequent calls to any of the fetch methods do not invalidate the returned rows.
- */
- std::vector fetch_many(std::size_t count, error_code& err, error_info& info);
+ /// Fetches several rows, up to a maximum (sync with error code version).
+ std::vector read_many(std::size_t count, error_code& err, error_info& info);
- /**
- * \brief Fetches several rows, up to a maximum
- * (sync with exceptions version).
- * \details The returned rows may outlive the resultset.
- * Subsequent calls to any of the fetch methods do not invalidate the returned rows.
- */
- std::vector fetch_many(std::size_t count);
+ /// Fetches several rows, up to a maximum (sync with exceptions version).
+ std::vector read_many(std::size_t count);
/**
* \brief Fetches several rows, up to a maximum
* (async without [reflink error_info] version).
- * \details The returned rows may outlive the resultset.
- * Subsequent calls to any of the fetch methods do not invalidate the returned rows.
- *
+ * \details
* The handler signature for this operation is
- * `void(boost::mysql::error_code, std::vector)`.
+ * `void(boost::mysql::error_code, std::vector)`.
*/
template <
- BOOST_ASIO_COMPLETION_TOKEN_FOR(void(error_code, std::vector))
+ BOOST_ASIO_COMPLETION_TOKEN_FOR(void(error_code, std::vector))
CompletionToken
BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)
>
- BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, std::vector))
- async_fetch_many(
+ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, std::vector))
+ async_read_many(
std::size_t count,
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
)
{
- return async_fetch_many(count, shared_info(), std::forward(token));
+ return async_read_many(count, shared_info(), std::forward(token));
}
/**
* \brief Fetches several rows, up to a maximum
* (async with [reflink error_info] version).
- * \details The returned rows may outlive the resultset.
- * Subsequent calls to any of the fetch methods do not invalidate the returned rows.
- *
+ * \details
* The handler signature for this operation is
- * `void(boost::mysql::error_code, std::vector)`.
+ * `void(boost::mysql::error_code, std::vector)`.
*/
template <
- BOOST_ASIO_COMPLETION_TOKEN_FOR(void(error_code, std::vector))
+ BOOST_ASIO_COMPLETION_TOKEN_FOR(void(error_code, std::vector))
CompletionToken
BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)
>
- BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, std::vector))
- async_fetch_many(
+ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, std::vector))
+ async_read_many(
std::size_t count,
error_info& output_info,
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
);
- /**
- * \brief Fetches all available rows (sync with error code version).
- * \details The returned rows may outlive the resultset.
- * Subsequent calls to any of the fetch methods do not invalidate the returned rows.
- */
- std::vector fetch_all(error_code& err, error_info& info);
+ /// Fetches all available rows (sync with error code version).
+ std::vector read_all(error_code& err, error_info& info);
- /**
- * \brief Fetches all available rows (sync with exceptions version).
- * \details The returned rows may outlive the resultset.
- * Subsequent calls to any of the fetch methods do not invalidate the returned rows.
- */
- std::vector fetch_all();
+ /// Fetches all available rows (sync with exceptions version).
+ std::vector read_all();
/**
* \brief Fetches all available rows (async without [reflink error_info] version).
- * \details The returned rows may outlive the resultset.
- * Subsequent calls to any of the fetch methods do not invalidate the returned rows.
- *
+ * \details
* The handler signature for this operation is
- * `void(boost::mysql::error_code, std::vector)`.
+ * `void(boost::mysql::error_code, std::vector)`.
*/
template <
- BOOST_ASIO_COMPLETION_TOKEN_FOR(void(error_code, std::vector))
+ BOOST_ASIO_COMPLETION_TOKEN_FOR(void(error_code, std::vector))
CompletionToken
BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)
>
- BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, std::vector))
- async_fetch_all(CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type))
+ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, std::vector))
+ async_read_all(CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type))
{
- return async_fetch_all(shared_info(), std::forward(token));
+ return async_read_all(shared_info(), std::forward(token));
}
/**
* \brief Fetches all available rows (async with [reflink error_info] version).
- * \details The returned rows may outlive the resultset.
- * Subsequent calls to any of the fetch methods do not invalidate the returned rows.
- *
+ * \details
* The handler signature for this operation is
- * `void(boost::mysql::error_code, std::vector)`.
+ * `void(boost::mysql::error_code, std::vector)`.
*/
template <
- BOOST_ASIO_COMPLETION_TOKEN_FOR(void(error_code, std::vector))
+ BOOST_ASIO_COMPLETION_TOKEN_FOR(void(error_code, std::vector))
CompletionToken
BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)
>
- BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, std::vector))
- async_fetch_all(
+ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, std::vector))
+ async_read_all(
error_info& output_info,
CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)
);
diff --git a/include/boost/mysql/row.hpp b/include/boost/mysql/row.hpp
index 1ef628a4..e9039319 100644
--- a/include/boost/mysql/row.hpp
+++ b/include/boost/mysql/row.hpp
@@ -18,51 +18,64 @@ namespace boost {
namespace mysql {
/**
- * \brief Represents a row returned from a query.
- * \details Call [refmem row values] to get the actual sequence of
- * [reflinl value]s the row contains.
+ * \brief Represents a row returned from a database operation.
+ * \details A row is a collection of values, plus a buffer holding memory
+ * for the string [reflink value]s.
+ *
+ * Call [refmem row values] to get the actual sequence of
+ * [reflink value]s the row contains.
*
* There will be the same number of values and in the same order as fields
* in the SQL query that produced the row. You can get more information
* about these fields using [refmem resultset fields].
*
- * If any of the values is a string, it will point to an externally owned piece of memory.
- * Thus, the [reflink row] class is not owning, as opposed to [reflink owning_row].
- * Note that, in any case, the sequence of [reflink value]s __is__ owned by the
- * row object. The distinction applies only to the memory pointed to by
- * string [reflink value]s.
+ * If any of the values is a string, it will be represented as a `string_view`
+ * pointing into the row's buffer. These string values will be valid as long as
+ * the [reflink row] object containing the memory they point to is alive and valid. Concretely:
+ * - Destroying the row object invalidates the string values.
+ * - Move assigning against the row invalidates the string values.
+ * - Calling [refmem row clear] invalidates the string values.
+ * - Move-constructing a [reflink row] from the current row does **not**
+ * invalidate the string values.
+ *
+ * Default constructible and movable, but not copyable.
*/
class row
{
std::vector values_;
+ detail::bytestring buffer_;
public:
- /// Default and initializing constructor.
- row(std::vector&& values = {}):
- values_(std::move(values)) {};
+ row() = default;
+ row(std::vector&& values, detail::bytestring&& buffer) noexcept :
+ values_(std::move(values)), buffer_(std::move(buffer)) {};
+ row(const row&) = delete;
+ row(row&&) = default;
+ row& operator=(const row&) = delete;
+ row& operator=(row&&) = default;
+ ~row() = default;
/// Accessor for the sequence of values.
const std::vector& values() const noexcept { return values_; }
/// Accessor for the sequence of values.
std::vector& values() noexcept { return values_; }
-};
-/**
- * \brief A row that owns a chunk of memory for its string values.
- * \details Default constructible and movable, but not copyable.
- */
-class owning_row : public row
-{
- detail::bytestring buffer_;
-public:
- owning_row() = default;
- owning_row(std::vector&& values, detail::bytestring&& buffer) :
- row(std::move(values)), buffer_(std::move(buffer)) {};
- owning_row(const owning_row&) = delete;
- owning_row(owning_row&&) = default;
- owning_row& operator=(const owning_row&) = delete;
- owning_row& operator=(owning_row&&) = default;
- ~owning_row() = default;
+ /**
+ * \brief Clears the row object.
+ * \details Clears the value array and the memory buffer associated to this row.
+ * After calling this operation, [refmem row values] will be the empty array. Any
+ * pointers, references and iterators to elements in [refmem row values] will be invalidated.
+ * Any string values using the memory held by this row will also become invalid.
+ */
+ void clear() noexcept
+ {
+ values_.clear();
+ buffer_.clear();
+ }
+
+ // Private, do not use
+ const detail::bytestring& buffer() const noexcept { return buffer_; }
+ detail::bytestring& buffer() noexcept { return buffer_; }
};
/**
diff --git a/test/common/test_common.hpp b/test/common/test_common.hpp
index 3171ab0a..0056322e 100644
--- a/test/common/test_common.hpp
+++ b/test/common/test_common.hpp
@@ -33,7 +33,7 @@ std::vector make_value_vector(Types&&... args)
template
row makerow(Types&&... args)
{
- return row(make_value_vector(std::forward(args)...));
+ return row(make_value_vector(std::forward(args)...), {});
}
inline date makedate(int num_years, unsigned num_months, unsigned num_days)
diff --git a/test/integration/close_statement.cpp b/test/integration/close_statement.cpp
index 0131b8cb..c7252148 100644
--- a/test/integration/close_statement.cpp
+++ b/test/integration/close_statement.cpp
@@ -23,7 +23,7 @@ BOOST_MYSQL_NETWORK_TEST(existing_or_closed_statement, network_fixture, network_
// Verify it works fine
auto exec_result = net->execute_statement(stmt.value, {});
exec_result.validate_no_error();
- exec_result.value.fetch_all(); // clean all packets sent for this execution
+ exec_result.value.read_all(); // clean all packets sent for this execution
// Close the statement
auto close_result = net->close_statement(stmt.value);
diff --git a/test/integration/connect.cpp b/test/integration/connect.cpp
index 60e4f6dc..933efd78 100644
--- a/test/integration/connect.cpp
+++ b/test/integration/connect.cpp
@@ -23,7 +23,7 @@ BOOST_MYSQL_NETWORK_TEST(physical_and_handshake_ok, network_fixture, network_ssl
// We are able to query
auto query_result = sample.net->query(this->conn, "SELECT 1");
query_result.validate_no_error();
- query_result.value.fetch_all(); // discard any result
+ query_result.value.read_all(); // discard any result
}
BOOST_MYSQL_NETWORK_TEST(physical_error, network_fixture, network_ssl_gen)
diff --git a/test/integration/database_types.cpp b/test/integration/database_types.cpp
index 4a989670..4c15b34f 100644
--- a/test/integration/database_types.cpp
+++ b/test/integration/database_types.cpp
@@ -18,7 +18,6 @@ using namespace boost::unit_test;
using boost::mysql::value;
using boost::mysql::field_metadata;
using boost::mysql::field_type;
-using boost::mysql::row;
using boost::mysql::datetime;
using boost::mysql::make_values;
@@ -591,7 +590,7 @@ BOOST_DATA_TEST_CASE_F(database_types_fixture, query, data::make(all_samples))
// Execute it
auto result = conn.query(query);
- auto rows = result.fetch_all();
+ auto rows = result.read_all();
// Validate the received metadata
validate_meta(result.fields(), {sample.mvalid});
@@ -614,7 +613,7 @@ BOOST_DATA_TEST_CASE_F(database_types_fixture, prepared_statement, data::make(al
// Execute it with the provided parameters
auto result = stmt.execute(make_values(sample.row_id));
- auto rows = result.fetch_all();
+ auto rows = result.read_all();
// Validate the received metadata
validate_meta(result.fields(), {sample.mvalid});
@@ -661,7 +660,7 @@ BOOST_DATA_TEST_CASE_F(database_types_fixture,
// Execute it with the provided parameters
auto result = stmt.execute(make_values(sample.row_id, sample.expected_value));
- auto rows = result.fetch_all();
+ auto rows = result.read_all();
// Validate the returned value
BOOST_TEST_REQUIRE(rows.size() == 1);
diff --git a/test/integration/integration_test_common.hpp b/test/integration/integration_test_common.hpp
index 66bb06f3..25c91924 100644
--- a/test/integration/integration_test_common.hpp
+++ b/test/integration/integration_test_common.hpp
@@ -114,13 +114,13 @@ struct network_fixture
// make the testing environment more stable and speed up the tests
void start_transaction()
{
- this->conn.query("START TRANSACTION").fetch_all();
+ this->conn.query("START TRANSACTION").read_all();
}
std::int64_t get_table_size(const std::string& table)
{
return this->conn.query("SELECT COUNT(*) FROM " + table)
- .fetch_all().at(0).values().at(0).template get();
+ .read_all().at(0).values().at(0).template get();
}
};
diff --git a/test/integration/network_functions.hpp b/test/integration/network_functions.hpp
index 41ae4a50..20f9753d 100644
--- a/test/integration/network_functions.hpp
+++ b/test/integration/network_functions.hpp
@@ -100,9 +100,9 @@ public:
virtual network_result execute_statement(
prepared_statement_type&, const std::vector&) = 0;
virtual network_result close_statement(prepared_statement_type&) = 0;
- virtual network_result fetch_one(resultset_type&) = 0;
- virtual network_result> fetch_many(resultset_type&, std::size_t count) = 0;
- virtual network_result> fetch_all(resultset_type&) = 0;
+ virtual network_result read_one(resultset_type&, row&) = 0;
+ virtual network_result> read_many(resultset_type&, std::size_t count) = 0;
+ virtual network_result> read_all(resultset_type&) = 0;
virtual network_result quit(connection_type&) = 0;
virtual network_result close(connection_type&) = 0;
};
diff --git a/test/integration/network_functions/async_callback.cpp b/test/integration/network_functions/async_callback.cpp
index e926371a..bcbf9be8 100644
--- a/test/integration/network_functions/async_callback.cpp
+++ b/test/integration/network_functions/async_callback.cpp
@@ -14,10 +14,8 @@ using namespace boost::mysql::test;
using boost::mysql::connection_params;
using boost::mysql::error_code;
using boost::mysql::error_info;
-using boost::mysql::errc;
using boost::mysql::value;
using boost::mysql::row;
-using boost::mysql::owning_row;
namespace
{
@@ -152,29 +150,30 @@ public:
return stmt.async_close(info, std::move(h));
});
}
- network_result fetch_one(
- resultset_type& r
+ network_result read_one(
+ resultset_type& r,
+ row& output
) override
{
- return impl([&](handler h, error_info& info) {
- return r.async_fetch_one(info, std::move(h));
+ return impl([&](handler h, error_info& info) {
+ return r.async_read_one(output, info, std::move(h));
});
}
- network_result> fetch_many(
+ network_result> read_many(
resultset_type& r,
std::size_t count
) override
{
- return impl>([&](handler> h, error_info& info) {
- return r.async_fetch_many(count, info, std::move(h));
+ return impl>([&](handler> h, error_info& info) {
+ return r.async_read_many(count, info, std::move(h));
});
}
- network_result> fetch_all(
+ network_result> read_all(
resultset_type& r
) override
{
- return impl>([&](handler> h, error_info& info) {
- return r.async_fetch_all(info, std::move(h));
+ return impl>([&](handler> h, error_info& info) {
+ return r.async_read_all(info, std::move(h));
});
}
network_result quit(
@@ -301,29 +300,30 @@ public:
return stmt.async_close(std::move(h));
});
}
- network_result fetch_one(
- resultset_type& r
+ network_result read_one(
+ resultset_type& r,
+ row& output
) override
{
- return impl([&](handler h) {
- return r.async_fetch_one(std::move(h));
+ return impl([&](handler h) {
+ return r.async_read_one(output, std::move(h));
});
}
- network_result> fetch_many(
+ network_result> read_many(
resultset_type& r,
std::size_t count
) override
{
- return impl>([&](handler> h) {
- return r.async_fetch_many(count, std::move(h));
+ return impl>([&](handler> h) {
+ return r.async_read_many(count, std::move(h));
});
}
- network_result> fetch_all(
+ network_result> read_all(
resultset_type& r
) override
{
- return impl>([&](handler> h) {
- return r.async_fetch_all(std::move(h));
+ return impl>([&](handler> h) {
+ return r.async_read_all(std::move(h));
});
}
network_result quit(
diff --git a/test/integration/network_functions/async_coroutine.cpp b/test/integration/network_functions/async_coroutine.cpp
index ae3cdf57..a2d05cc6 100644
--- a/test/integration/network_functions/async_coroutine.cpp
+++ b/test/integration/network_functions/async_coroutine.cpp
@@ -16,7 +16,6 @@ using boost::mysql::error_info;
using boost::mysql::errc;
using boost::mysql::value;
using boost::mysql::row;
-using boost::mysql::owning_row;
using boost::asio::yield_context;
namespace
@@ -124,29 +123,30 @@ public:
return no_result();
});
}
- network_result fetch_one(
- resultset_type& r
+ network_result read_one(
+ resultset_type& r,
+ row& output
) override
{
return impl(r, [&](yield_context yield, error_info& info) {
- return r.async_fetch_one(info, yield);
+ return r.async_read_one(output, info, yield);
});
}
- network_result> fetch_many(
+ network_result> read_many(
resultset_type& r,
std::size_t count
) override
{
return impl(r, [&](yield_context yield, error_info& info) {
- return r.async_fetch_many(count, info, yield);
+ return r.async_read_many(count, info, yield);
});
}
- network_result> fetch_all(
+ network_result> read_all(
resultset_type& r
) override
{
return impl(r, [&](yield_context yield, error_info& info) {
- return r.async_fetch_all(info, yield);
+ return r.async_read_all(info, yield);
});
}
network_result quit(
@@ -269,29 +269,30 @@ public:
return no_result();
});
}
- network_result fetch_one(
- resultset_type& r
+ network_result read_one(
+ resultset_type& r,
+ row& output
) override
{
return impl(r, [&](yield_context yield) {
- return r.async_fetch_one(yield);
+ return r.async_read_one(output, yield);
});
}
- network_result> fetch_many(
+ network_result> read_many(
resultset_type& r,
std::size_t count
) override
{
return impl(r, [&](yield_context yield) {
- return r.async_fetch_many(count, yield);
+ return r.async_read_many(count, yield);
});
}
- network_result