Skip to content

Commit

Permalink
Boost 1.87: new discussion and examples (part 1)
Browse files Browse the repository at this point in the history
Added tutorials 2, 3 and 4
Changed most of the discussion to use any_connection
Added examples on disabling TLS, multi-function operations, multi-queries and transactions
All examples now use any_connection
Automate parts of example qbk generation

Partially addresses #365 and #366
  • Loading branch information
anarthal authored Nov 4, 2024
1 parent c57103b commit f6c1192
Show file tree
Hide file tree
Showing 101 changed files with 6,058 additions and 6,738 deletions.
105 changes: 69 additions & 36 deletions doc/qbk/00_main.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

[library Boost.MySQL
[quickbook 1.7]
[copyright 2019 - 2023 Ruben Perez]
[copyright 2019 - 2024 Ruben Perez]
[id mysql]
[purpose MySQL client library]
[license
Expand Down Expand Up @@ -56,16 +56,16 @@
[def __Context__ [@boost:/libs/context/index.html Boost.Context]]
[def __Self__ [@boost:/libs/mysql/index.html Boost.MySQL]]
[def __boost_optional__ [@boost:/libs/optional/index.html `boost::optional`]]
[def __see_error_handling__ See [link mysql.error_handling this section] for more info on error handling.]
[def __assume_setup__ This example assumes you have gone through the [link mysql.examples.setup setup].]
[def __Describe__ [@boost:/libs/describe/index.html Boost.Describe]]
[def __Pfr__ [@boost:/libs/pfr/index.html Boost.Pfr]]
[def __ssl_context__ [asioreflink ssl__context ssl::context]]

[/ MySQL stuff]
[def __Mysql__ [@https://www.mysql.com/ MySQL]]
[def __sql_mode__ [mysqllink sql-mode.html `sql_mode`]]
[def __allow_invalid_dates__ [mysqllink sql-mode.html#sqlmode_allow_invalid_dates `ALLOW_INVALID_DATES`]]
[def __strict_sql__ [mysqllink sql-mode.html#sql-mode-strict strict SQL mode]]
[def __time_zone__ [mysqllink server-system-variables.html#sysvar_time_zone `time_zone`]]
[def __SET_NAMES__ [mysqllink set-names.html `SET NAMES`]]
[def __TINYINT__ [mysqllink integer-types.html `TINYINT`]]
[def __SMALLINT__ [mysqllink integer-types.html `SMALLINT`]]
[def __MEDIUMINT__ [mysqllink integer-types.html `MEDIUMINT`]]
Expand Down Expand Up @@ -124,47 +124,80 @@ BEGIN
END
```]

[import ../../test/integration/include/test_integration/snippets/describe.hpp]
[import ../../test/integration/test/snippets/overview.cpp]
[import ../../test/integration/test/snippets/dynamic.cpp]
[import ../../test/integration/test/snippets/static.cpp]
[/ AUTOGENERATED IMPORTS BEGIN ]
[import ../../example/1_tutorial/1_sync.cpp]
[import ../../example/1_tutorial/2_async.cpp]
[import ../../example/1_tutorial/3_with_params.cpp]
[import ../../example/1_tutorial/4_static_interface.cpp]
[import ../../example/2_simple/prepared_statements.cpp]
[import ../../example/2_simple/timeouts.cpp]
[import ../../example/2_simple/multi_queries_transactions.cpp]
[import ../../example/2_simple/disable_tls.cpp]
[import ../../example/2_simple/tls_certificate_verification.cpp]
[import ../../example/2_simple/metadata.cpp]
[import ../../example/2_simple/multi_function.cpp]
[import ../../example/2_simple/callbacks.cpp]
[import ../../example/2_simple/coroutines_cpp11.cpp]
[import ../../example/2_simple/unix_socket.cpp]
[import ../../example/2_simple/batch_inserts.cpp]
[import ../../example/2_simple/batch_inserts_generic.cpp]
[import ../../example/2_simple/dynamic_filters.cpp]
[import ../../example/2_simple/patch_updates.cpp]
[import ../../example/2_simple/source_script.cpp]
[import ../../example/2_simple/pipeline.cpp]
[import ../../example/3_advanced/connection_pool/main.cpp]
[import ../../example/3_advanced/connection_pool/types.hpp]
[import ../../example/3_advanced/connection_pool/repository.hpp]
[import ../../example/3_advanced/connection_pool/repository.cpp]
[import ../../example/3_advanced/connection_pool/handle_request.hpp]
[import ../../example/3_advanced/connection_pool/handle_request.cpp]
[import ../../example/3_advanced/connection_pool/server.hpp]
[import ../../example/3_advanced/connection_pool/server.cpp]
[import ../../example/3_advanced/connection_pool/log_error.hpp]
[import ../../test/integration/test/snippets/prepared_statements.cpp]
[import ../../test/integration/test/snippets/multi_resultset.cpp]
[import ../../test/integration/test/snippets/connection_establishment.cpp]
[import ../../test/integration/test/snippets/charsets.cpp]
[import ../../test/integration/test/snippets/sql_formatting_custom.cpp]
[import ../../test/integration/test/snippets/multi_function.cpp]
[import ../../test/integration/test/snippets/tutorials.cpp]
[import ../../test/integration/test/snippets/metadata.cpp]
[import ../../test/integration/test/snippets/charsets.cpp]
[import ../../test/integration/test/snippets/time_types.cpp]
[import ../../test/integration/test/snippets/any_connection.cpp]
[import ../../test/integration/test/snippets/connection_pool.cpp]
[import ../../test/integration/test/snippets/sql_formatting.cpp]
[import ../../test/integration/test/snippets/sql_formatting_custom.cpp]
[import ../../test/integration/test/snippets/time_types.cpp]
[import ../../test/integration/test/snippets/pipeline.cpp]
[import ../../test/integration/test/snippets/sql_formatting.cpp]
[import ../../test/integration/test/snippets/overview.cpp]
[import ../../test/integration/test/snippets/dynamic.cpp]
[import ../../test/integration/test/snippets/multi_resultset.cpp]
[import ../../test/integration/test/snippets/static.cpp]
[/ AUTOGENERATED IMPORTS END ]
[import ../../test/integration/include/test_integration/snippets/describe.hpp]

[include 01_intro.qbk]
[include 02_integrating.qbk]
[include 03_tutorial.qbk]
[include 03_1_tutorial_sync.qbk]
[include 03_2_tutorial_async.qbk]
[include 03_3_tutorial_with_params.qbk]
[include 03_4_tutorial_static_interface.qbk]
[include 04_overview.qbk]
[include 05_dynamic_interface.qbk]
[include 06_static_interface.qbk]
[include 07_queries.qbk]
[include 08_prepared_statements.qbk]
[include 09_multi_resultset.qbk]
[include 10_multi_function.qbk]
[include 11_metadata.qbk]
[include 12_async.qbk]
[include 13_ssl.qbk]
[include 14_other_streams.qbk]
[include 15_error_handling.qbk]
[include 16_connparams.qbk]
[include 17_reconnecting.qbk]
[include 18_charsets.qbk]
[include 19_time_types.qbk]
[include 20_any_connection.qbk]
[include 21_connection_pool.qbk]
[include 22_sql_formatting.qbk]
[include 23_sql_formatting_advanced.qbk]
[include 24_pipeline.qbk]
[include 24_examples.qbk]
[include 05_connection_establishment.qbk]
[include 06_sql_formatting.qbk]
[include 07_prepared_statements.qbk]
[include 08_dynamic_interface.qbk]
[include 09_static_interface.qbk]
[include 10_multi_resultset.qbk]
[include 11_multi_function.qbk]
[include 12_connection_pool.qbk]
[include 13_async.qbk]
[include 14_error_handling.qbk]
[include 15_sql_formatting_advanced.qbk]
[include 16_metadata.qbk]
[include 17_charsets.qbk]
[include 18_time_types.qbk]
[/ TODO: re-enable this
[include 19_templated_connection.qbk] ]
[include 20_pipeline.qbk]
[include 21_examples.qbk]



[section:ref Reference]
Expand Down
2 changes: 1 addition & 1 deletion doc/qbk/02_integrating.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ target_link_libraries(main PRIVATE Boost::charconv Threads::Threads OpenSSL::Cry
an older version, use the `Boost::headers` target, instead.
]

If you're happy with header-only mode, have a look at [link mysql.tutorial the tutorial]
If you're happy with header-only mode, have a look at [link mysql.tutorial_sync the first tutorial]
or [link mysql.examples any of the examples] to learn how to use the library.

[endsect]
Expand Down
119 changes: 119 additions & 0 deletions doc/qbk/03_1_tutorial_sync.qbk
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
[/
Copyright (c) 2019-2024 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:tutorial_sync Tutorial 1: hello world!]

In this first tutorial, we will write a simple program
to demonstrate the basic concepts. We will connect to the server
and issue the query `SELECT "Hello World!"`.

To run this tutorial, you need a running MySQL server listening
in localhost on port 3306 (the default one). You should have
the credentials of a valid MySQL user (username and password).
No further setup is needed.

The full program listing for this tutorial can be found [link mysql.examples.tutorial_sync here].

We will follow these steps:

# Create a connection object.
# Establish a session with the server.
# Issue the query.
# Use the rows generated by the query.
# Close the connection.

This tutorial uses synchronous functions with exceptions,
as they're easy to use. In subsequent tutorials, we will
learn how to use asynchronous functions, which are more versatile.

[heading Namespace conventions]

All functions and classes reside within the `boost::mysql` namespace.
To reduce verbosity, all examples and code samples use the following namespace aliases:

[tutorial_sync_namespaces]




[heading Connection object]

Like most Asio-based applications, we need to create a
[asioreflink io_context io_context] object before anything else.
An `io_context` is an execution context: it contains an event loop,
file descriptor states, timers and other items required to perform I/O.
Most applications should only create a single `io_context`, even when
multiple MySQL connections are needed.

We then create an [reflink any_connection], which represents a single connection
to a MySQL server:

[tutorial_sync_connection]




[heading Connecting to the server]

[refmem any_connection connect] establishes a client session with the server.
It takes a [reflink connect_params] object with the required
information to establish a session:

[tutorial_sync_connect]




[heading Issuing the SQL query]

[refmem any_connection execute] accepts a string containing
the SQL query to run and sends it to the server for execution.
It returns a [reflink results] object, containing the rows returned by the query:

[tutorial_sync_query]




[heading Obtaining the results]

[reflink results] is a class that holds the result of a query in memory.
To obtain the value we selected, we can write:

[tutorial_sync_results]

Let's break this into steps:

* [refmem results rows] returns all the rows that this object contains.
It returns a [reflink rows_view], which is a 2D matrix-like structure.
* `result.rows().at(0)` returns the first row, represented as a [reflink row_view].
* `result.rows().at(0).at(0)` returns the first field in the first row. This is a
[reflink field_view], a variant-like class that can hold any type allowed in MySQL.
* The obtained `field_view` is streamed to `std::cout`.




[heading Closing the connection]

Once we are done with the connection, we can close it by calling
[refmem any_connection close]. Note that
this will send a final quit packet to the MySQL server to notify
we are closing the connection, and thus may fail.

[tutorial_sync_close]




[heading Next steps]

Full program listing for this tutorial is [link mysql.examples.tutorial_sync here].

You can now proceed to [link mysql.tutorial_async the next tutorial].

[endsect]
105 changes: 105 additions & 0 deletions doc/qbk/03_2_tutorial_async.qbk
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
[/
Copyright (c) 2019-2024 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:tutorial_async Tutorial 2: going async with C++20 coroutines]

In the [link mysql.tutorial_sync previous tutorial] we used
synchronous functions. They are simple, but have a number of limitations:

* They aren't as versatile as async functions. For example, there is no way
to set a timeout to a sync operation.
* They don't scale well. Since sync functions block the calling thread until they complete,
you need to create OS threads to achieve parallelism. This doesn't scale well
and leads to the inherent complexities of multi-threaded programs.
* Some classes (like [reflink connection_pool]) only offer an async interface.

For this reason, we recommend to always use async functions.
All Asio-compatible libraries (including this one) allow async
programming using a variety of styles. In this chapter, we will
explain how to use C++20 coroutines because they are the simplest to use.

[note
Still not using C++20? Don't worry, you can use
[link mysql.examples.coroutines_cpp11 stackful coroutines] and
[link mysql.examples.callbacks callbacks] even in C++11.
]



[heading What is a coroutine?]

Roughly speaking, it's a function that can suspend and resume, keeping local variables
alive in the process. Suspension happens when reaching a `co_await` expression.
These usually appear when the program performs an I/O operation.
When an expression like this is encountered, the following happens:

# The coroutine initiates the I/O operation.
# The coroutine suspends, passing control back to the `io_context` (that is, the event loop).
# While the I/O operation is in progress, the `io_context` may run other operations,
like other coroutines.
# When the I/O operation completes, the `io_context` resumes the coroutine
immediately after the `co_await` expression.





[heading Transforming sync code into coroutines]

Recall the following code from our previous tutorial:

[tutorial_sync_main]

To transform this code into a coroutine, we need to:

* Extract it to a separate function returning `boost::asio::awaitable<void>`.
* Replace sync functions (like [refmem any_connection connect]) by async ones
(like [refmem any_connection async_connect]).
* Place a `co_await` operator in front of each I/O operation.

Doing this, we have:

[tutorial_async_coro]

Note that the coroutine doesn't create or return explicitly any
`boost::asio::awaitable<void>` object - this is handled by the compiler.
The return type actually marks the function as being a coroutine.
`void` here means that the coroutine doesn't return anything.

If any of the above I/O operations fail, an exception is thrown.
You can prevent this by [link mysql.overview.errors using `asio::redirect_error`].




[heading Running our coroutine]

As in the previous tutorial, we first need to create an `io_context` and a connection:

[tutorial_async_connection]

To run a coroutine, use [asioreflink co_spawn co_spawn]:

[tutorial_async_co_spawn]

Note that this will only schedule the coroutine. To actually run
it, we need to call `io_context::run`. This will block the calling
thread until all the scheduled coroutines and I/O operations complete:

[tutorial_async_run]





[heading Next steps]

Full program listing for this tutorial is [link mysql.examples.tutorial_async here].

You can now proceed to [link mysql.tutorial_with_params the next tutorial].

[endsect]
Loading

0 comments on commit f6c1192

Please sign in to comment.