Files
rippled/doc/exchange.html
Vinnie Falco 9708a12607 Squashed 'src/soci/' content from commit 6e9312c
git-subtree-dir: src/soci
git-subtree-split: 6e9312c4bb3748907bd28d62c40feca42878cfef
2015-03-18 19:36:00 -07:00

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 &lt;&lt; "select count(*) from person", into(count);
string name;
sql &lt;&lt; "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 &lt;&lt; "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 &lt;&lt; "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 &lt;&lt; "insert into person(id, firstname, lastname) values(:id, :fn, :ln)",
use(personId), use(firstName), use(lastName);
sql &lt;&lt; "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 &lt;&lt; "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 &lt;&lt; "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 &lt;&lt; "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 &lt;&lt; "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 &amp; name = getNameFromSomewhere();
sql &lt;&lt; "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 &lt;&lt; "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 &lt;&lt; "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&lt;string&gt; names(100);
vector&lt;indicator&gt; inds;
sql &lt;&lt; "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&lt;int&gt; ids;
vector&lt;string&gt; names;
vector&lt;indicator&gt; nameIndicators;
for (int i = 0; i != 10; ++i)
{
ids.push_back(i);
names.push_back("");
nameIndicators.push_back(i_null);
}
sql &lt;&lt; "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&lt;char&gt;</code></li>
<li><code>std::vector&lt;short&gt;</code></li>
<li><code>std::vector&lt;int&gt;</code></li>
<li><code>std::vector&lt;unsigned long&gt;</code></li>
<li><code>std::vector&lt;long long&gt;</code></li>
<li><code>std::vector&lt;double&gt;</code></li>
<li><code>std::vector&lt;std::string&gt;</code></li>
<li><code>std::vector&lt;std::tm&gt;</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 &lt;&lt; "select * from some_table", into(r);
std::ostringstream doc;
doc &lt;&lt; "&lt;row&gt;" &lt;&lt; std::endl;
for(std::size_t i = 0; i != r.size(); ++i)
{
const column_properties &amp; props = r.get_properties(i);
doc &lt;&lt; '&lt;' &lt;&lt; props.get_name() &lt;&lt; '&gt;';
switch(props.get_data_type())
{
case dt_string:
doc &lt;&lt; r.get&lt;std::string&gt;(i);
break;
case dt_double:
doc &lt;&lt; r.get&lt;double&gt;(i);
break;
case dt_integer:
doc &lt;&lt; r.get&lt;int&gt;(i);
break;
case dt_long_long:
doc &lt;&lt; r.get&lt;long long&gt;(i);
break;
case dt_unsigned_long_long:
doc &lt;&lt; r.get&lt;unsigned long long&gt;(i);
break;
case dt_date:
std::tm when = r.get&lt;std::tm&gt;(i);
doc &lt;&lt; asctime(&amp;when);
break;
}
doc &lt;&lt; "&lt;/" &lt;&lt; props.get_name() &lt;&lt; '&gt;' &lt;&lt; std::endl;
}
doc &lt;&lt; "&lt;/row&gt;";
</pre>
<p>The type <code>T</code> parameter that should be passed to
<code>row::get&lt;T&gt;()</code> depends on the SOCI data type that
is returned from
<code>column_properties::get_data_type()</code>.</p>
<p><code>row::get&lt;T&gt;()</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&lt;T&gt;</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 &lt;&lt; "select name from some_table where id = 1", into(r);
if (r.get_indicator(0) != soci::i_null)
{
std::cout &lt;&lt; r.get&lt;std::string&gt;(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 &lt;&lt; "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 &lt;&gt;
struct type_conversion&lt;MyInt&gt;
{
typedef int base_type;
static void from_base(int i, indicator ind, MyInt &amp; mi)
{
if (ind == i_null)
{
throw soci_error("Null value not allowed for this type");
}
mi.set(i);
}
static void to_base(const MyInt &amp; mi, int &amp; i, indicator &amp; ind)
{
i = mi.get();
ind = i_ok;
}
};
}
</pre>
<p>The above specialization for <code>soci::type_conversion&lt;MyInt&gt;</code> is enough
to enable the following:</p>
<pre class="example">
MyInt i;
sql &lt;&lt; "select count(*) from person", into(i);
cout &lt;&lt; "We have " &lt;&lt; i.get() &lt;&lt; " 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&lt;T&gt;</code> and <code>use_type&lt;T&gt;</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&lt;&gt;
struct type_conversion&lt;Person&gt;
{
typedef values base_type;
static void from_base(values const &amp; v, indicator /* ind */, Person &amp; p)
{
p.id = v.get&lt;int&gt;("ID");
p.firstName = v.get&lt;std::string&gt;("FIRST_NAME");
p.lastName = v.get&lt;std::string&gt;("LAST_NAME");
// p.gender will be set to the default value "unknown"
// when the column is null:
p.gender = v.get&lt;std::string&gt;("GENDER", "unknown");
// alternatively, the indicator can be tested directly:
// if (v.indicator("GENDER") == i_null)
// {
// p.gender = "unknown";
// }
// else
// {
// p.gender = v.get&lt;std::string&gt;("GENDER");
// }
}
static void to_base(const Person &amp; p, values &amp; v, indicator &amp; 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 &lt;&lt; "insert into person(id, first_name, last_name) "
"values(:ID, :FIRST_NAME, :LAST_NAME)", use(p);
Person p1;
sql &lt;&lt; "select * from person", into(p1);
assert(p1.id == 1);
assert(p1.firstName + p.lastName == "PatSmith");
assert(p1.gender == "unknown");
p.firstName = "Patricia";
sql &lt;&lt; "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 &lt;&lt; "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 &copy; 2010-2013 Mateusz Loskot</p>
<p class="copyright">Copyright &copy; 2012 Vadim Zeitlin</p>
<p class="copyright">Copyright &copy; 2004-2008 Maciej Sobczak, Stephen Hutton</p>
</body>
</html>