...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
To use the dynamic interface, use the results
class. results
is an in-memory
representation of a resultset. We can depict it like this (this is actually
a simplified representation, since some
statements may return more than one resultset).
We can see that results::rows
returns a matrix-like object, containing the retrieved rows. This section is
dedicated on diving deeper on how to use these objects.
This matrix-like structure is composed of variant-like objects called fields. Field objects are capable of representing any value retrieved from MySQL.
This library defines the following classes to work with rows and fields:
field
The smallest unit of data. A single "cell" in a MySQL table. This is an owning, variant-like type.
field_view
Like field
, but non-owning.
row
An owning, vector
-like
collection of fields.
row_view
Like row
, but non-owning.
rows
An owning, matrix-like collection of fields. Represents several rows of the same size in an optimized way.
rows_view
Like rows
, but non-owning.
results::rows
returns a rows_view
object. The memory for the rows is owned by the results
object. Indexing the returned view also returns view objects:
// Populate a results object results 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(); // Indexing a rows_view yields a row_view. The underlying memory is owned by the results object row_view first_row = all_rows.at(0); // Indexing a row_view yields a field_view. The underlying memory is owned by the results object field_view first_field = first_row.at(0); // Contains the string "Hello world"
Views behave similarly to std::string_view
.
You must make sure that you don't use a view after the storage it points to
has gone out of scope. In this case, you must not use any of the views after
the results
object has gone
out of scope.
As it happens with std::string_view
, you can take ownership of a
view using its owning counterpart:
// You may use all_rows_owning after result has gone out of scope rows all_rows_owning{all_rows}; // You may use first_row_owning after result has gone out of scope row first_row_owning{first_row}; // You may use first_field_owning after result has gone out of scope field first_field_owning{first_field};
field
and field_view
are specialized variant-like types that can hold any type you may find in a
MySQL table. Once you obtain a field, you can access its contents using the
following functions:
kind
,
which returns a field_kind
enum.
field::is_xxx
.
field::as_xxx
and field::get_xxx
. The as_xxx
functions are checked (they will throw an exception if the actual type
doesn't match), while the get_xxx
are unchecked (they result in undefined behavior on type mismatch).
For example:
results 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" if (f.is_string()) { // we know it's a string, unchecked access string_view s = f.get_string(); std::cout << s << std::endl; // Use the string as required } else { // Oops, something went wrong - schema mismatch? } // Alternative: use the as_xxx accessor f = result.rows().at(0).at(1); std::int64_t value = f.as_int64(); // Checked access. Throws if f doesn't contain an int std::cout << value << std::endl; // Use the int as required
NULL
values are represented
as field objects having kind() == field_kind::null
. You
can check whether a value is NULL
or not using is_null
.
This is how NULL
s are typically
handled:
results result; // Create some test data conn.execute( R"%( CREATE TEMPORARY TABLE products ( id VARCHAR(50) PRIMARY KEY, description VARCHAR(256) ) )%", result ); conn.execute("INSERT INTO products VALUES ('PTT', 'Potatoes'), ('CAR', NULL)", result); // Retrieve the data. Note that some fields are NULL conn.execute("SELECT id, description FROM products", result); for (row_view r : result.rows()) { field_view description_fv = r.at(1); if (description_fv.is_null()) { // Handle the NULL value // Note: description_fv.is_string() will return false here; NULL is represented as a separate // type std::cout << "No description for product_id " << r.at(0) << std::endl; } else { // Handle the non-NULL case. Get the underlying value and use it as you want // If there is any schema mismatch (and description was not defined as VARCHAR), this will // throw string_view description = description_fv.as_string(); // Use description as required std::cout << "product_id " << r.at(0) << ": " << description << std::endl; } }
Every MySQL type is mapped to a single C++ type. The following table shows these mappings:
|
C++ type |
MySQL types |
|
|
|
---|---|---|---|---|---|
|
|
||||
|
|
Unsigned |
|||
|
|
|
|||
|
|
||||
|
|
||||
|
|
||||
|
|||||
|
|||||
|
|||||
|
Any of the above, when they're |
No character set conversion is applied on strings. They are provided as the
server sends them. If you've issued a "SET
NAMES <charset-name>"
statement, strings will be
encoded according to <charset-name>
.
For details, see this section.
field_view
is to field
what std::string_view
is to std::string
.
field_view
s are cheap to create
and to copy, as they are small objects and don't perform any memory allocations.
They are also immutable. On the other hand, field
s
may be more expensive to create and copy, as they may perform memory allocations.
field
s are mutable.
field
and field_view
use the same underlying types for scalars. For strings and blobs, field
uses the owning types std::string
and blob
,
while field_view
uses the reference
types string_view
and blob_view
.
field
accessors return references,
which allow you to mutate the underlying object:
field f("my_string"); // constructs a field that owns the string "my_string" std::string& s = f.as_string(); // s points into f's storage s.push_back('2'); // f now holds "my_string2"
You can also mutate a field
using the assignment operator. This allows you to also change the underlying
type of a field
:
field f("my_string"); // constructs a field that owns the string "my_string" f = 42; // destroys "my_string" and stores the value 42 as an int64
You can use both with the dynamic interface. Please refer to the sections on multi-resultset operations and multi-function operations for more information.
The following table reflects mapping from database types to C++ types. The
range column shows the range of values that MySQL admits for that type. This
library guarantees that any field retrieved from the database honors that range.
The column_type
column shows
what metadata::type
would return for a column of that type.
MySQL type |
|
C++ type |
Range |
|
Considerations |
---|---|---|---|---|---|
|
|
|
|
1 byte integer |
|
|
|
|
|
|
1 byte integer |
|
|
|
|
2 byte integer |
|
|
|
|
|
|
2 byte integer |
|
|
|
|
3 byte integer |
|
|
|
|
|
|
3 byte integer |
|
|
|
|
4 byte integer |
|
|
|
|
|
|
4 byte integer |
|
|
|
|
8 byte integer |
|
|
|
|
|
|
8 byte integer |
|
|
[ |
|
1 byte integer type used to represent years Zero is often employed to represent invalid year values. We represent zero year as a numeric 0. |
|
|
|
Depends on the bitset width. Max |
|
A bitset between 1 and 64 bits wide. |
|
|
|
IEEE 754 |
|
4 byte floating point type |
|
|
|
IEEE 754 |
|
8 byte floating point type |
|
|
[ |
|
|||
|
[ |
|
Time point type without time zone, with a resolution of one microsecond. |
||
|
[ |
|
Time point type with a resolution of one microsecond. |
||
|
|
Signed time duration, with a resolution of one microsecond. |
|||
|
|
|
Fixed-size character string. |
||
|
|
|
Variable size character string with a maximum size. |
||
|
|
|
|
Variable size character string. |
|
|
|
|
Character string with a fixed set of possible values (only one possible). |
||
|
|
|
Character string with a fixed set of possible values (many possible). |
||
|
|
|
A serialized JSON value of any type.
Note that |
||
|
|
Depends on the column definition |
|
A fixed precision numeric value. In this case, the string will contain
the textual representation of the number (e.g. the string This type is mapped to a string to avoid losing precision. |
|
|
|
Fixed-size blob. |
|||
|
|
Variable size blob with a maximum size. |
|||
|
|
|
Variable size blob. |
||
|
|
Any of the spatial data types. The string contains the binary representation of the geometry type. |