mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-19 10:35:50 +00:00
759 lines
26 KiB
HTML
759 lines
26 KiB
HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
|
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en'>
|
|
<head>
|
|
<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type" />
|
|
<link rel="stylesheet" type="text/css" href="style.css" />
|
|
<title>SOCI - exchanging data</title>
|
|
</head>
|
|
|
|
<body>
|
|
<p class="banner">SOCI - The C++ Database Access Library</p>
|
|
|
|
<h2>Exchanging data</h2>
|
|
|
|
<div class="navigation">
|
|
<a href="#bind_local">Binding local data</a><br />
|
|
<div class="navigation-indented">
|
|
<a href="#bind_output">Binding output data</a><br />
|
|
<a href="#bind_input">Binding input data</a><br />
|
|
<a href="#bind_position">Binding by position</a><br />
|
|
<a href="#bind_name">Binding by name</a><br />
|
|
</div>
|
|
<a href="exchange.html#data_states">Handling of nulls and other conditions</a><br />
|
|
<div class="navigation-indented">
|
|
<a href="#indicators">Indicators</a><br />
|
|
</div>
|
|
<a href="#types">Types</a><br />
|
|
<div class="navigation-indented">
|
|
<a href="#static">Static binding</a><br />
|
|
<a href="#static_bulk">Static binding for bulk operations</a><br />
|
|
<a href="#dynamic">Dynamic resultset binding</a><br />
|
|
<a href="#custom_types">Extending with user-provided datatypes</a><br />
|
|
<a href="#object_relational">Object-relational mapping</a><br />
|
|
</div>
|
|
<a href="#blob">Large objects (BLOBs)</a><br />
|
|
</div>
|
|
|
|
<h3 id="bind_local">Binding local data</h3>
|
|
|
|
<div class="note">
|
|
<p><span class="note">Note:</span>
|
|
The Oracle documentation uses two terms: <i>defining</i> (for
|
|
instructing the library where the <i>output</i> data should go) and <i>binding</i>
|
|
(for the <i>input</i> data and <i>input/output</i> PL/SQL
|
|
parameters). For the sake of simplicity, SOCI uses the term <i>binding</i>
|
|
for both of these.</p>
|
|
</div>
|
|
|
|
<h4 id="bind_output">Binding output data</h4>
|
|
|
|
<p>The <code>into</code> expression is used to add binding information to
|
|
the statement:</p>
|
|
|
|
<pre class="example">
|
|
int count;
|
|
sql << "select count(*) from person", into(count);
|
|
|
|
string name;
|
|
sql << "select name from person where id = 7", into(name);
|
|
</pre>
|
|
|
|
<p>In the above examples, some data is retrieved from the database and
|
|
transmitted <i>into</i> the given local variable.</p>
|
|
|
|
<p>There should be as many <code>into</code> elements as there are
|
|
expected columns in the result (see <a href="#dynamic">dynamic
|
|
resultset binding</a> for the exception to this rule).</p>
|
|
|
|
<h4 id="bind_input">Binding input data</h4>
|
|
|
|
<p>The <code>use</code> expression associates the SQL placeholder (written with colon) with the local data:</p>
|
|
|
|
<pre class="example">
|
|
int val = 7;
|
|
sql << "insert into numbers(val) values(:val)", use(val);
|
|
</pre>
|
|
|
|
<p>In the above statement, the first "val" is a column name (assuming
|
|
that
|
|
there is appropriate table <code>numbers</code> with this column), the
|
|
second "val" (with colon) is a
|
|
placeholder and its name is ignored here, and the third "val" is a name
|
|
of local variable.</p>
|
|
|
|
<p>To better understand the meaning of each "val" above, consider also:</p>
|
|
|
|
<pre class="example">
|
|
int number = 7;
|
|
sql << "insert into numbers(val) values(:blabla)", use(number);
|
|
</pre>
|
|
|
|
<p>Both examples above will insert the value of some local variable into
|
|
the table <code>numbers</code> - we say that the local variable is <i>used</i> in the SQL statement.</p>
|
|
|
|
<p>There should be as many <code>use</code> elements as there are
|
|
parameters used in the SQL query.</p>
|
|
|
|
<div class="note">
|
|
<p><span class="note">Portability note:</span></p>
|
|
<p>Older versions of the PostgreSQL client API do not allow to use input
|
|
parameters at all. In order to compile SOCI with those old client
|
|
libraries, define the <code>SOCI_POSTGRESQL_NOPARAMS</code> preprocessor
|
|
name passing <code>-DSOCI_POSTGRESQL_NOPARAMS=ON</code> variable to CMake.</p>
|
|
</div>
|
|
|
|
<h4 id="bind_position">Binding by position</h4>
|
|
|
|
<p>If there is more output or input "holes" in the single statement, it
|
|
is possible to use many <code>into</code> and <code>use</code>
|
|
expressions, separated by commas, where each expression will be
|
|
responsible for the consecutive "hole" in the statement:</p>
|
|
|
|
<pre class="example">
|
|
string firstName = "John", lastName = "Smith";
|
|
int personId = 7;
|
|
|
|
sql << "insert into person(id, firstname, lastname) values(:id, :fn, :ln)",
|
|
use(personId), use(firstName), use(lastName);
|
|
|
|
sql << "select firstname, lastname from person where id = :id",
|
|
into(firstName), into(lastName), use(personId);
|
|
</pre>
|
|
|
|
<p>In the code above, the order of "holes" in the SQL statement and the
|
|
order of <code>into</code> and <code>use</code> expression should
|
|
match.</p>
|
|
|
|
<h4 id="bind_name">Binding by name</h4>
|
|
|
|
<p>The SQL placeholders that have their names (with colon) can be bound
|
|
by name to clearly associate the local variable with the given placeholder.</p>
|
|
|
|
<p>This explicit naming allows to use different order of elements:</p>
|
|
|
|
<pre class="example">
|
|
string firstName = "John", lastName = "Smith";
|
|
int personId = 7;
|
|
sql << "insert into person(id, firstname, lastname) values(:id, :fn, :ln)",
|
|
use(firstName, "fn"), use(lastName, "ln"), use(personId, "id");
|
|
</pre>
|
|
|
|
<p>or bind the same local data to many "holes" at the same time:</p>
|
|
|
|
<pre class="example">
|
|
string addr = "...";
|
|
sql << "update person"
|
|
" set mainaddress = :addr, contactaddress = :addr"
|
|
" where id = 7",
|
|
use(addr, "addr");
|
|
</pre>
|
|
|
|
<div class="note">
|
|
<p><span class="note">Object lifetime and immutability:</span></p>
|
|
<p>SOCI assumes that local variables provided as <code>use</code> elements
|
|
live at least as long at it takes to execute the whole statement.
|
|
In short statement forms like above, the statement is executed <i>sometime</i>
|
|
at the end of the full expression and the whole process is driven by the invisible
|
|
temporary object handled by the library. If the data provided by user comes
|
|
from another temporary variable, it might be possible for the compiler to arrange
|
|
them in a way that the user data will be destroyed <i>before</i> the statement will
|
|
have its chance to execute, referencing objects that no longer exist:</p>
|
|
<pre class="example">
|
|
// dangerous code:
|
|
|
|
string getNameFromSomewhere();
|
|
|
|
sql << "insert into person(name) values(:n)",
|
|
use(getNameFromSomewhere());
|
|
</pre>
|
|
<p>In the above example, the data passed to the database comes from the temporary
|
|
variable that is a result of call to <code>getNameFromSomewhere</code> - this
|
|
should be avoided and named variables should be used to ensure safe lifetime relations:</p>
|
|
<pre class="example">
|
|
// safe code:
|
|
|
|
string getNameFromSomewhere();
|
|
|
|
string name = getNameFromSomewhere();
|
|
sql << "insert into person(name) values(:n)",
|
|
use(name);
|
|
</pre>
|
|
|
|
<p>It is still possible to provide <code>const</code> data for use elements.
|
|
Note that some database servers, like Oracle, allow PL/SQL procedures to modify their
|
|
in/out parameters - this is detected by the SOCI library and an error is reported
|
|
if the database attempts to modify the <code>use</code> element that holds <code>const</code>
|
|
data.</p>
|
|
<p>The above example can be ultimately written in the following way:</p>
|
|
<pre class="example">
|
|
// safe and efficient code:
|
|
|
|
string getNameFromSomewhere();
|
|
|
|
const string & name = getNameFromSomewhere();
|
|
sql << "insert into person(name) values(:n)",
|
|
use(name);
|
|
</pre>
|
|
</div>
|
|
|
|
<div class="note">
|
|
<p><span class="note">Portability notes:</span></p>
|
|
<p>The PostgreSQL backend allows to use the "native" PostgreSQL way of
|
|
naming parameters in the query, which is by numbers like <code>$1</code>,
|
|
<code>$2</code>, <code>$3</code>, etc. In fact, the backend <i>rewrites</i>
|
|
the given query to the native form - and this is also one of the very few places
|
|
where SOCI intrudes into the SQL query. For portability reasons, it is
|
|
recommended to use named parameters, as shown in the examples above.<br />
|
|
The query rewriting can be switched off by compiling the backend with
|
|
the <code>SOCI_POSTGRESQL_NOBINDBYNAME</code> name defined (pass
|
|
<code>-DSOCI_POSTGRESQL_NOBINDBYNAME=ON</code> variable to CMake).
|
|
Note that in this case it is also necessary to define <code>SOCI_POSTGRESQL_NOPREPARE</code>
|
|
(controlled by CMake variable <code>-DSOCI_POSTGRESQL_NOPREPARE=ON</code>),
|
|
because statement preparation relies on successful query rewriting.
|
|
In practice, both macros will be needed for PostgreSQL server older than 8.0.</p>
|
|
</div>
|
|
|
|
<h3 id="data_states">Handling nulls and other conditions</h3>
|
|
|
|
<h4 id="indicators">Indicators</h4>
|
|
|
|
<p>In order to support null values and other conditions which are not
|
|
real errors, the concept of <i>indicator</i> is provided.</p>
|
|
|
|
<p>For example, when the following SQL query is executed:</p>
|
|
<pre class="example">
|
|
select name from person where id = 7
|
|
</pre>
|
|
|
|
<p>there are three possible outcomes:</p>
|
|
<ol>
|
|
<li>there is a person with id = 7 and his name is returned</li>
|
|
<li>there is a person with id = 7, but he has no name (his name is
|
|
null in the database table)</li>
|
|
<li>there is no such person</li>
|
|
</ol>
|
|
|
|
<p>Whereas the first alternative is easy to handle, the other two are more
|
|
complex. Moreover, they are not necessarily errors from the
|
|
application's point of view and what's more interesting, they are <i>different</i>
|
|
and the application may wish to detect which is the case.<br />
|
|
The following example does this:</p>
|
|
|
|
<pre class="example">
|
|
string name;
|
|
indicator ind;
|
|
|
|
sql << "select name from person where id = 7", into(name, ind);
|
|
|
|
if (sql.got_data())
|
|
{
|
|
switch (ind)
|
|
{
|
|
case i_ok:
|
|
// the data was returned without problems
|
|
break;
|
|
case i_null:
|
|
// there is a person, but he has no name (his name is null)
|
|
break;
|
|
case i_truncated:
|
|
// the name was returned only in part,
|
|
// because the provided buffer was too short
|
|
// (not possible with std::string, but possible with char* and char[])
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// no such person in the database
|
|
}
|
|
</pre>
|
|
|
|
<p>The use of indicator variable is optional, but if it is not used and
|
|
the result would be <code>i_null</code>,
|
|
then the exception is thrown. This means that you should use indicator
|
|
variables everywhere where the application logic (and database schema)
|
|
allow the "attribute not set" condition.</p>
|
|
|
|
<p>Indicator variables can be also used when binding input data, to
|
|
control whether the data is to be used as provided, or explicitly
|
|
overrided to be null:</p>
|
|
|
|
<pre class="example">
|
|
int id = 7;
|
|
string name;
|
|
indicator ind = i_null;
|
|
sql << "insert into person(id, name) values(:id, :name)",
|
|
use(id), use(name, ind);
|
|
</pre>
|
|
|
|
<p>In the above example, the row is inserted with <code>name</code>
|
|
attribute set to null.</p>
|
|
|
|
<p>Indicator variables can also be used in conjunction with vector
|
|
based insert, update, and select statements:</p>
|
|
|
|
<pre class="example">
|
|
vector<string> names(100);
|
|
vector<indicator> inds;
|
|
sql << "select name from person where id = 7", into(names, inds);
|
|
</pre>
|
|
|
|
<p>The above example retrieves first 100 rows of data (or less). The
|
|
initial size of <code>names</code> vector provides the (maximum)
|
|
number of rows that should be read. Both vectors will be
|
|
automatically resized according to the number of rows that were
|
|
actually read.</p>
|
|
|
|
<p>The following example inserts null for each value of name:</p>
|
|
|
|
<pre class="example">
|
|
vector<int> ids;
|
|
vector<string> names;
|
|
vector<indicator> nameIndicators;
|
|
|
|
for (int i = 0; i != 10; ++i)
|
|
{
|
|
ids.push_back(i);
|
|
names.push_back("");
|
|
nameIndicators.push_back(i_null);
|
|
}
|
|
|
|
sql << "insert into person(id, name) values(:id, :name)",
|
|
use(ids), use(name, nameIndicators);
|
|
</pre>
|
|
|
|
<p>See also <a href="boost.html">Integration with Boost</a> to learn
|
|
how the Boost.Optional library can be used to handle null data conditions
|
|
in a more natural way.</p>
|
|
|
|
<h3 id="types">Types</h3>
|
|
|
|
<h4 id="static">Static type binding</h4>
|
|
|
|
<p>The static binding for types is most useful when the types used in
|
|
the database are known at compile time - this was already presented
|
|
above with the help of <code>into</code> and <code>use</code>
|
|
functions.</p>
|
|
|
|
<p>The following types are currently supported for use with <code>into</code>
|
|
and <code>use</code> expressions:</p>
|
|
<ul>
|
|
<li><code>char</code> (for character values)</li>
|
|
<li><code>short</code>, <code>int</code>, <code>unsigned
|
|
long</code>, <code>long long</code>, <code>double</code> (for numeric values)</li>
|
|
<li><code>char*</code>, <code>char[]</code>, <code>std::string</code>
|
|
(for string values)</li>
|
|
<li><code>std::tm</code><code></code> (for datetime
|
|
values)</li>
|
|
<li><code>soci::statement</code> (for nested statements and PL/SQL
|
|
cursors)</li>
|
|
<li><code>soci::blob</code> (for Binary Large OBjects)</li>
|
|
<li><code>soci::row_id</code> (for row identifiers)</li>
|
|
</ul>
|
|
|
|
<p>See the test code that accompanies the library to see how each of
|
|
these types is used.</p>
|
|
|
|
<h4 id="static_bulk">Static type binding for bulk operations</h4>
|
|
|
|
<p>Bulk inserts, updates, and selects are supported through the
|
|
following <code>std::vector</code> based into and use types:
|
|
</p>
|
|
<ul>
|
|
<li><code>std::vector<char></code></li>
|
|
<li><code>std::vector<short></code></li>
|
|
<li><code>std::vector<int></code></li>
|
|
<li><code>std::vector<unsigned long></code></li>
|
|
<li><code>std::vector<long long></code></li>
|
|
<li><code>std::vector<double></code></li>
|
|
<li><code>std::vector<std::string></code></li>
|
|
<li><code>std::vector<std::tm></code></li>
|
|
</ul>
|
|
|
|
<p>Use of the vector based types mirrors that of the standard types, with
|
|
the size of the vector used to specify the number of records to process
|
|
at a time. See below for examples.</p>
|
|
|
|
<p>Note that bulk operations are supported only for <code>std::vector</code>s of the types listed above.</p>
|
|
|
|
<h4 id="dynamic">Dynamic resultset binding</h4>
|
|
|
|
<p>For certain applications it is desirable to be able to select data from
|
|
arbitrarily structured tables (e.g. via "<code>select * from ...</code>") and format the
|
|
resulting data based upon its type.
|
|
SOCI supports this through the <code>soci::row</code> and <code>soci::column_properties</code>
|
|
classes.</p>
|
|
|
|
<p>Data is selected into a <code>row</code> object, which holds <code>column_properties</code>
|
|
objects describing
|
|
the attributes of data contained in each column. Once the data type for each
|
|
column is known, the data can be formatted appropriately.</p>
|
|
|
|
<p>For example, the code below creates an XML document from a selected row
|
|
of data from an arbitrary table:</p>
|
|
|
|
<pre class="example">
|
|
row r;
|
|
sql << "select * from some_table", into(r);
|
|
|
|
std::ostringstream doc;
|
|
doc << "<row>" << std::endl;
|
|
for(std::size_t i = 0; i != r.size(); ++i)
|
|
{
|
|
const column_properties & props = r.get_properties(i);
|
|
|
|
doc << '<' << props.get_name() << '>';
|
|
|
|
switch(props.get_data_type())
|
|
{
|
|
case dt_string:
|
|
doc << r.get<std::string>(i);
|
|
break;
|
|
case dt_double:
|
|
doc << r.get<double>(i);
|
|
break;
|
|
case dt_integer:
|
|
doc << r.get<int>(i);
|
|
break;
|
|
case dt_long_long:
|
|
doc << r.get<long long>(i);
|
|
break;
|
|
case dt_unsigned_long_long:
|
|
doc << r.get<unsigned long long>(i);
|
|
break;
|
|
case dt_date:
|
|
std::tm when = r.get<std::tm>(i);
|
|
doc << asctime(&when);
|
|
break;
|
|
}
|
|
|
|
doc << "</" << props.get_name() << '>' << std::endl;
|
|
}
|
|
doc << "</row>";
|
|
</pre>
|
|
|
|
<p>The type <code>T</code> parameter that should be passed to
|
|
<code>row::get<T>()</code> depends on the SOCI data type that
|
|
is returned from
|
|
<code>column_properties::get_data_type()</code>.</p>
|
|
<p><code>row::get<T>()</code>
|
|
throws an exception of type
|
|
<code>std::bad_cast</code> if an incorrect type <code>T</code> is
|
|
requested.</p>
|
|
|
|
<table border="1" cellpadding="5" cellspacing="0">
|
|
<tbody>
|
|
<tr>
|
|
<th>SOCI Data Type</th>
|
|
<th><code>row::get<T></code> specialization</th>
|
|
</tr>
|
|
<tr>
|
|
<td><code>dt_double</code></td>
|
|
<td><code>double</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>dt_integer</code></td>
|
|
<td><code>int</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>dt_long_long</code></td>
|
|
<td><code>long long</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>dt_unsigned_long_long</code></td>
|
|
<td><code>unsigned long long</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>dt_string</code></td>
|
|
<td><code>std::string</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>dt_date</code></td>
|
|
<td><code>std::tm</code><code></code></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<p>The mapping of underlying database column types to SOCI datatypes is database specific.
|
|
See the <a href="backends/index.html">backend documentation</a> for details.</p>
|
|
|
|
<p>The <code>row</code> also provides access to indicators for each column:</p>
|
|
|
|
<pre>
|
|
row r;
|
|
sql << "select name from some_table where id = 1", into(r);
|
|
if (r.get_indicator(0) != soci::i_null)
|
|
{
|
|
std::cout << r.get<std::string>(0);
|
|
}
|
|
</pre>
|
|
|
|
<p>It is also possible to extract data from the <code>row</code> object using its stream-like
|
|
interface, where each extracted variable should have matching type respective to its position in the chain:</p>
|
|
|
|
<pre class="example">
|
|
row r;
|
|
sql << "select name, address, age from persons where id = 123", into(r);
|
|
|
|
string name, address;
|
|
int age;
|
|
|
|
r >> name >> address >> age;
|
|
</pre>
|
|
|
|
<p>Note, however, that this interface is <i>not</i> compatible with the standard
|
|
<code>std::istream</code> class and that it is only possible to extract a single row at a time
|
|
- for "safety" reasons the row boundary is preserved and it is necessary to perform the
|
|
<code>fetch</code> operation explicitly for each consecutive row
|
|
(see <a href="statements.html">next page</a>).</p>
|
|
|
|
<h4 id="custom_types">Extending SOCI to support custom (user-defined) C++ types</h4>
|
|
|
|
<p>SOCI can be easily extended with support for user-defined datatypes.</p>
|
|
|
|
<p>The extension mechanism relies on appropriate specialization of the <code>type_conversion</code>
|
|
struct that converts to and from one of the following SOCI base types:</p>
|
|
|
|
<ul>
|
|
<li><code>double</code></li>
|
|
<li><code>int</code></li>
|
|
<li><code>long long</code></li>
|
|
<li><code>unsigned long long</code></li>
|
|
<li><code>std::string</code></li>
|
|
<li><code>char</code></li>
|
|
<li><code>std::tm</code></li>
|
|
</ul>
|
|
|
|
<p>There are three required class members for a valid <code>type_conversion</code>
|
|
specialization:</p>
|
|
<ul>
|
|
<li>the <code>base_type</code> type definition, aliasing either one of the base types <i>or another ser-defined type</i></li>
|
|
<li>the <code>from_base()</code> static member function, converting from
|
|
the base type</li>
|
|
<li>the <code>to_base()</code> static member function, converting to the
|
|
base type</li>
|
|
</ul>
|
|
|
|
<p>Note that no database-specific code is required to define user conversion.</p>
|
|
|
|
<p>The following example shows how the user can extend SOCI to
|
|
support his own type <code>MyInt</code>, which here is some wrapper
|
|
for the fundamental <code>int</code> type:</p>
|
|
|
|
<pre class="example">
|
|
class MyInt
|
|
{
|
|
public:
|
|
MyInt() {}
|
|
MyInt(int i) : i_(i) {}
|
|
|
|
void set(int i) { i_ = i; }
|
|
int get() const { return i_; }
|
|
|
|
private:
|
|
int i_;
|
|
};
|
|
|
|
namespace soci
|
|
{
|
|
template <>
|
|
struct type_conversion<MyInt>
|
|
{
|
|
typedef int base_type;
|
|
|
|
static void from_base(int i, indicator ind, MyInt & mi)
|
|
{
|
|
if (ind == i_null)
|
|
{
|
|
throw soci_error("Null value not allowed for this type");
|
|
}
|
|
|
|
mi.set(i);
|
|
}
|
|
|
|
static void to_base(const MyInt & mi, int & i, indicator & ind)
|
|
{
|
|
i = mi.get();
|
|
ind = i_ok;
|
|
}
|
|
};
|
|
}
|
|
</pre>
|
|
|
|
<p>The above specialization for <code>soci::type_conversion<MyInt></code> is enough
|
|
to enable the following:</p>
|
|
|
|
<pre class="example">
|
|
MyInt i;
|
|
|
|
sql << "select count(*) from person", into(i);
|
|
|
|
cout << "We have " << i.get() << " persons in the database.\n";
|
|
</pre>
|
|
|
|
<p>Note that there is a number of types from the Boost library integrated with SOCI out of the box, see <a href="boost.html">Integration with Boost</a> for complete description. Use these as examples of conversions for more complext data types.</p>
|
|
|
|
<p>Note also that user-defined datatypes are not supported with <a href="#static_bulk">bulk data transfer</a>.</p>
|
|
|
|
<p>Another possibility to extend SOCI with custom data types is to use
|
|
the <code>into_type<T></code> and <code>use_type<T></code>
|
|
class templates, which specializations can be user-provided. These
|
|
specializations need to implement the interface defined by,
|
|
respectively, the <code>into_type_base</code> and <code>use_type_base</code>
|
|
classes.</p>
|
|
|
|
<p>Note that when specializing these template classes the only convention
|
|
is that when the indicator
|
|
variable is used (see below), it should appear in the second position.
|
|
Please refer to the library source code to see how this is done for the
|
|
standard types.</p>
|
|
|
|
<h4 id="object_relational">Object-relational mapping</h4>
|
|
|
|
<p>SOCI provides a class called <code>values</code> specifically to
|
|
enable object-relational mapping via <code>type_conversion</code>
|
|
specializations.</p>
|
|
|
|
<p>For example, the following code maps a <code>Person</code> object to
|
|
and from a
|
|
database table containing columns <code>"ID"</code>, <code>"FIRST_NAME"</code>, <code>"LAST_NAME"</code>, and
|
|
<code>"GENDER"</code>.</p>
|
|
|
|
<p>Note that the mapping is non-invasive - the <code>Person</code> object
|
|
itself does not contain any SOCI-specific code:</p>
|
|
|
|
<pre class="example">
|
|
struct Person
|
|
{
|
|
int id;
|
|
std::string firstName;
|
|
std::string lastName;
|
|
std::string gender;
|
|
};
|
|
|
|
namespace soci
|
|
{
|
|
template<>
|
|
struct type_conversion<Person>
|
|
{
|
|
typedef values base_type;
|
|
|
|
static void from_base(values const & v, indicator /* ind */, Person & p)
|
|
{
|
|
p.id = v.get<int>("ID");
|
|
p.firstName = v.get<std::string>("FIRST_NAME");
|
|
p.lastName = v.get<std::string>("LAST_NAME");
|
|
|
|
// p.gender will be set to the default value "unknown"
|
|
// when the column is null:
|
|
p.gender = v.get<std::string>("GENDER", "unknown");
|
|
|
|
// alternatively, the indicator can be tested directly:
|
|
// if (v.indicator("GENDER") == i_null)
|
|
// {
|
|
// p.gender = "unknown";
|
|
// }
|
|
// else
|
|
// {
|
|
// p.gender = v.get<std::string>("GENDER");
|
|
// }
|
|
}
|
|
|
|
static void to_base(const Person & p, values & v, indicator & ind)
|
|
{
|
|
v.set("ID", p.id);
|
|
v.set("FIRST_NAME", p.firstName);
|
|
v.set("LAST_NAME", p.lastName);
|
|
v.set("GENDER", p.gender, p.gender.empty() ? i_null : i_ok);
|
|
ind = i_ok;
|
|
}
|
|
};
|
|
}
|
|
</pre>
|
|
|
|
<p>With the above <code>type_conversion</code> specialization in place, it
|
|
is possible to use <code>Person</code> directly with SOCI:</p>
|
|
|
|
<pre class="example">
|
|
session sql(oracle, "service=db1 user=scott password=tiger");
|
|
|
|
Person p;
|
|
p.id = 1;
|
|
p.lastName = "Smith";
|
|
p.firstName = "Pat";
|
|
sql << "insert into person(id, first_name, last_name) "
|
|
"values(:ID, :FIRST_NAME, :LAST_NAME)", use(p);
|
|
|
|
Person p1;
|
|
sql << "select * from person", into(p1);
|
|
assert(p1.id == 1);
|
|
assert(p1.firstName + p.lastName == "PatSmith");
|
|
assert(p1.gender == "unknown");
|
|
|
|
p.firstName = "Patricia";
|
|
sql << "update person set first_name = :FIRST_NAME "
|
|
"where id = :ID", use(p);
|
|
</pre>
|
|
|
|
<div class="note">
|
|
<p><span class="note">Note:</span> The <code>values</code>
|
|
class is currently not suited for use outside of <code>type_conversion</code>
|
|
specializations. It is specially designed to facilitate
|
|
object-relational mapping when used as shown above.</p>
|
|
</div>
|
|
|
|
<h3 id="blob">Large objects (BLOBs)</h3>
|
|
|
|
<p>The SOCI library provides also an interface for basic operations on
|
|
large objects (BLOBs - Binary Large OBjects).</p>
|
|
|
|
<pre class="example">
|
|
blob b(sql); // sql is a session object
|
|
sql << "select mp3 from mymusic where id = 123", into(b);
|
|
</pre>
|
|
|
|
<p>The following functions are provided in the <code>blob</code>
|
|
interface, mimicking the file-like operations:</p>
|
|
<ul>
|
|
<li><code>std::size_t get_len();</code></li>
|
|
<li><code>std::size_t read(std::size_t offset, char *buf, std::size_t
|
|
toRead);</code></li>
|
|
<li><code>std::size_t write(std::size_t offset, char const *buf,
|
|
std::size_t toWrite);</code></li>
|
|
<li><code>std::size_t append(char const *buf, std::size_t toWrite);</code></li>
|
|
<li><code>void trim(std::size_t newLen);</code></li>
|
|
</ul>
|
|
|
|
<p>The <code>offset</code> parameter is always counted from the beginning
|
|
of the BLOB's data.</p>
|
|
|
|
<div class="note">
|
|
<p><span class="note">Portability notes:</span></p>
|
|
<ol>
|
|
<li>The way to define BLOB table columns and create or destroy BLOB
|
|
objects in the database varies between different database engines.
|
|
Please see the SQL documentation relevant for the given server to learn
|
|
how this is actually done. The test programs provided with the SOCI
|
|
library can be also a simple source of full working examples.</li>
|
|
<li>The <code>trim</code> function is not currently available for
|
|
the PostgreSQL backend.</li>
|
|
</ol>
|
|
</div>
|
|
|
|
<table class="foot-links" border="0" cellpadding="2" cellspacing="2">
|
|
<tr>
|
|
<td class="foot-link-left">
|
|
<a href="basics.html">Previous (Connections and simple queries)</a>
|
|
</td>
|
|
<td class="foot-link-right">
|
|
<a href="statements.html">Next (Statements, procedures and transactions)</a>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<p class="copyright">Copyright © 2010-2013 Mateusz Loskot</p>
|
|
<p class="copyright">Copyright © 2012 Vadim Zeitlin</p>
|
|
<p class="copyright">Copyright © 2004-2008 Maciej Sobczak, Stephen Hutton</p>
|
|
</body>
|
|
</html>
|