1048 lines
47 KiB
XML
1048 lines
47 KiB
XML
<?xml version="1.0" encoding='UTF-8'?>
|
|
<!DOCTYPE sect1 PUBLIC "-//OASIS//DTD DocBook V4.2//EN"
|
|
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
|
|
|
<sect1 id="ssqls">
|
|
<title>Specialized SQL Structures</title>
|
|
|
|
<para>The Specialized SQL Structure (SSQLS) feature lets you easily
|
|
define C++ structures that match the form of your SQL tables. At the
|
|
most superficial level, an SSQLS has a member variable corresponding
|
|
to each field in the SQL table. But, an SSQLS also has several
|
|
methods, operators, and data members used by MySQL++’s internals to
|
|
provide neat functionality, which we cover in this chapter.</para>
|
|
|
|
<para>You define SSQLSes using the macros defined in
|
|
<filename>ssqls.h</filename>. This is the only MySQL++ header not
|
|
automatically included for you by <filename>mysql++.h</filename>. You
|
|
have to include it in code modules that use the SSQLS feature.</para>
|
|
|
|
|
|
<sect2 id="sql_create">
|
|
<title>sql_create</title>
|
|
|
|
<para>Let’s say you have the following SQL table:</para>
|
|
|
|
<programlisting>
|
|
CREATE TABLE stock (
|
|
item CHAR(30) NOT NULL,
|
|
num BIGINT NOT NULL,
|
|
weight DOUBLE NOT NULL,
|
|
price DECIMAL(6,2) NOT NULL,
|
|
sdate DATE NOT NULL,
|
|
description MEDIUMTEXT NULL)</programlisting>
|
|
|
|
<para>You can create a C++ structure corresponding to this table
|
|
like so:</para>
|
|
|
|
<programlisting>
|
|
sql_create_6(stock, 1, 6,
|
|
mysqlpp::sql_char, item,
|
|
mysqlpp::sql_bigint, num,
|
|
mysqlpp::sql_double, weight,
|
|
mysqlpp::sql_decimal, price,
|
|
mysqlpp::sql_date, sdate,
|
|
mysqlpp::Null<mysqlpp::sql_mediumtext>, description)</programlisting>
|
|
|
|
<para>This declares the <classname>stock</classname> structure,
|
|
which has a data member for each SQL column, using the same names.
|
|
The structure also has a number of member functions, operators and
|
|
hidden data members, but we won’t go into that just
|
|
now.</para>
|
|
|
|
<para>The parameter before each field name in the
|
|
<function>sql_create_#</function> call is the C++ data type that
|
|
will be used to hold that value in the SSQLS. While you could use
|
|
plain old C++ data types for most of these columns (<type>long
|
|
int</type> instead of <type>mysqlpp::sql_bigint</type>, for
|
|
example) it’s <link linkend="sql-types">best to use the
|
|
MySQL++ typedefs</link>.</para>
|
|
|
|
<para>Sometimes you have no choice but to use special MySQL++
|
|
data types to fully express the database schema. Consider
|
|
the <varname>description</varname> field. MySQL++’s
|
|
<type>sql_mediumtext</type> type is just an alias for
|
|
<type>std::string</type>, since we don’t need anything
|
|
fancier to hold a SQL <type>MEDIUMTEXT</type> value.
|
|
It’s the SQL NULL attribute that causes trouble:
|
|
it <link linkend="sql-null">has no equivalent in the C++
|
|
type system</link>. MySQL++ offers the <ulink type="classref"
|
|
url="Null"/> template, which bridges this difference between the
|
|
two type systems.</para>
|
|
|
|
<para>The general format of this macro is:</para>
|
|
|
|
<programlisting>
|
|
sql_create_#(NAME, COMPCOUNT, SETCOUNT, TYPE1, ITEM1, ... TYPE#, ITEM#)</programlisting>
|
|
|
|
<para>where # is the number of member variables,
|
|
<parameter>NAME</parameter> is the name of the structure you wish to
|
|
create, <parameter>TYPEx</parameter> is the type of a member
|
|
variable, and <parameter>ITEMx</parameter> is that variable’s
|
|
name.</para>
|
|
|
|
<para>The <parameter>COMPCOUNT</parameter> and
|
|
<parameter>SETCOUNT</parameter> arguments are described in the next
|
|
section.</para>
|
|
</sect2>
|
|
|
|
|
|
<sect2 id="ssqls-compare-init">
|
|
<title>SSQLS Comparison and Initialization</title>
|
|
|
|
<para>The <varname>sql_create_#</varname> macro adds member
|
|
functions and operators to each SSQLS that allow you to compare one
|
|
SSQLS instance to another. These functions compare the first
|
|
<parameter>COMPCOUNT</parameter> fields in the structure. In the
|
|
example above, <parameter>COMPCOUNT</parameter> is 1, so only the
|
|
<varname>item</varname> field will be checked when comparing two
|
|
<classname>stock</classname> structures.</para>
|
|
|
|
<para>This feature works best when your table’s
|
|
“key” fields are the first ones in the SSQLS and
|
|
you set <parameter>COMPCOUNT</parameter> equal to the number
|
|
of key fields. That way, a check for equality between two SSQLS
|
|
structures in your C++ code will give the same results as a check
|
|
for equality in SQL.</para>
|
|
|
|
<para><parameter>COMPCOUNT</parameter> must be at least 1. The
|
|
current implementation of <varname>sql_create_#</varname> cannot
|
|
create an SSQLS without comparison member functions.</para>
|
|
|
|
<para>Because our <classname>stock</classname> structure
|
|
is less-than-comparable, you can use it in STL algorithms
|
|
and containers that require this, such as STL’s associative
|
|
containers:</para>
|
|
|
|
<programlisting>
|
|
std::set<stock> result;
|
|
query.storein(result);
|
|
cout << result.lower_bound(stock("Hamburger"))->item << endl;</programlisting>
|
|
|
|
<para>This will print the first item in the result set that begins
|
|
with “Hamburger.”</para>
|
|
|
|
<para>The third parameter to <varname>sql_create_#</varname> is
|
|
<parameter>SETCOUNT</parameter>. If this is nonzero, it adds an
|
|
initialization constructor and a <function>set()</function> member
|
|
function taking the given number of arguments, for setting the first
|
|
<emphasis>N</emphasis> fields of the structure. For example, you
|
|
could change the above example like so:</para>
|
|
|
|
<programlisting>
|
|
sql_create_6(stock, 1, 2,
|
|
mysqlpp::sql_char, item,
|
|
mysqlpp::sql_bigint, num,
|
|
mysqlpp::sql_double, weight,
|
|
mysqlpp::sql_decimal, price,
|
|
mysqlpp::sql_date, sdate,
|
|
mysqlpp::Null<mysqlpp::sql_mediumtext>, description)
|
|
|
|
stock foo("Hotdog", 52);</programlisting>
|
|
|
|
<para>In addition to this 2-parameter constructor, this version
|
|
of the <classname>stock</classname> SSQLS will have a similar
|
|
2-parameter <function>set()</function> member function.</para>
|
|
|
|
<para>The <parameter>COMPCOUNT</parameter> and
|
|
<parameter>SETCOUNT</parameter> values cannot be equal. If they
|
|
are, the macro will generate two initialization constructors with
|
|
identical parameter lists, which is illegal in C++. You might be
|
|
asking, why does there need to be a constructor for comparison to
|
|
begin with? It’s often convenient to be able to say something
|
|
like <userinput>x == stock("Hotdog")</userinput>. This requires
|
|
that there be a constructor taking <parameter>COMPCOUNT</parameter>
|
|
arguments to create the temporary <classname>stock</classname>
|
|
instance used in the comparison.</para>
|
|
|
|
<para>This limitation is not a problem in practice. If you
|
|
want the same number of parameters in the initialization
|
|
constructor as the number of fields used in comparisons,
|
|
pass 0 for <parameter>SETCOUNT</parameter>. This suppresses
|
|
the duplicate constructor you’d get if you used the
|
|
<parameter>COMPCOUNT</parameter> value instead. This is most
|
|
useful in very small SSQLSes, since it’s easier for the
|
|
number of key fields to equal the number of fields you want to
|
|
compare on:</para>
|
|
|
|
<programlisting>
|
|
sql_create_1(stock_item, 1, 0, mysqlpp::sql_char, item)</programlisting>
|
|
</sect2>
|
|
|
|
|
|
<sect2 id="ssqls-retrieving">
|
|
<title>Retrieving data</title>
|
|
|
|
<para>Let’s put SSQLS to use. This is
|
|
<filename>examples/ssqls1.cpp</filename>:</para>
|
|
|
|
<programlisting><xi:include href="ssqls1.txt" parse="text"
|
|
xmlns:xi="http://www.w3.org/2001/XInclude"/></programlisting>
|
|
|
|
<para>Here is the stock.h header used by that example, and by
|
|
several others below:</para>
|
|
|
|
<programlisting><xi:include href="stock.txt" parse="text"
|
|
xmlns:xi="http://www.w3.org/2001/XInclude"/></programlisting>
|
|
|
|
<para>This example produces the same output as
|
|
<filename>simple1.cpp</filename> (see <xref linkend="simple"/>),
|
|
but it uses higher-level data structures paralleling the
|
|
database schema instead of MySQL++’s lower-level
|
|
generic data structures. It also uses MySQL++’s <xref
|
|
linkend="exceptions"/> for error handling instead of doing
|
|
everything inline. For small example programs like these, the
|
|
overhead of SSQLS and exceptions doesn’t pay off very
|
|
well, but in a real program, they end up working much better
|
|
than hand-rolled code.</para>
|
|
|
|
<para>Notice that we are only pulling a single column from the
|
|
<varname>stock</varname> table, but we are storing the rows in a
|
|
<type>std::vector<stock></type>. It may strike you as
|
|
inefficient to have five unused fields per record. It’s
|
|
easily remedied by defining a subset SSQLS:</para>
|
|
|
|
<programlisting>
|
|
sql_create_1(stock_subset,
|
|
1, 0,
|
|
string, item)
|
|
|
|
vector<stock_subset> res;
|
|
query.storein(res);
|
|
// ...etc...</programlisting>
|
|
|
|
<para>MySQL++ is flexible about populating
|
|
SSQLSes.<footnote><para>Programs built against versions of MySQL++
|
|
prior to 3.0 would crash at almost any mismatch between the database
|
|
schema and the SSQLS definition. It’s no longer necessary to
|
|
keep the data design in lock-step between the client and database
|
|
server. A mismatch can result in data loss, but not a
|
|
crash.</para></footnote> It works much like the Web, a design
|
|
that’s enabled the development of the largest distributed
|
|
system in the world. Just as a browser ignores tags and attributes
|
|
it doesn’t understand, you can populate an SSQLS from a query
|
|
result set containing columns that don’t exist in the SSQLS.
|
|
And as a browser uses sensible defaults when the page doesn’t
|
|
give explicit values, you can have an SSQLS with more fields defined
|
|
than are in the query result set, and these SSQLS fields will get
|
|
default values. (Zero for numeric types, <type>false</type> for
|
|
<type>bool</type>, and a type-specific default for anything more
|
|
complex, like <type>mysqlpp::DateTime</type>.)</para>
|
|
|
|
<para>In more concrete terms, the example above is able to
|
|
populate the <classname>stock</classname> objects using as
|
|
much information as it has, and leave the remaining fields at
|
|
their defaults. Conversely, you could also stuff the results
|
|
of <computeroutput>SELECT * FROM stock</computeroutput> into
|
|
the <classname>stock_subset</classname> SSQLS declared above;
|
|
the extra fields would just be ignored.</para>
|
|
|
|
<para>We’re trading run-time efficiency for flexibility
|
|
here, usually the right thing in a distributed system. Since MySQL
|
|
is a networked database server, many uses of it will qualify as
|
|
distributed systems. You can’t count on being able to update
|
|
both the server(s) and all the clients at the same time, so you
|
|
have to make them flexible enough to cope with differences while
|
|
the changes propagate. As long as the new database schema
|
|
isn’t too grossly different from the old, your programs
|
|
should continue to run until you get around to updating them to
|
|
use the new schema.</para>
|
|
|
|
<para>There’s a danger that this quiet coping behavior
|
|
may mask problems, but considering that the previous behavior
|
|
was for the program to crash when the database schema got out
|
|
of synch with the SSQLS definition, it’s likely to be
|
|
taken as an improvement.</para>
|
|
</sect2>
|
|
|
|
|
|
<sect2 id="ssqls-adding">
|
|
<title>Adding data</title>
|
|
|
|
<para>MySQL++ offers several ways to insert data in SSQLS form
|
|
into a database table.</para>
|
|
|
|
<sect3 id="ssqls-add-one">
|
|
<title>Inserting a Single Row</title>
|
|
|
|
<para>The simplest option is to insert a single row at a
|
|
time. This is <filename>examples/ssqls2.cpp</filename>:</para>
|
|
|
|
<programlisting><xi:include href="ssqls2.txt" parse="text"
|
|
xmlns:xi="http://www.w3.org/2001/XInclude"/></programlisting>
|
|
|
|
<para>That’s all there is to it! MySQL++ even takes care
|
|
of <link linkend="qescape">quoting and escaping</link> the
|
|
data when building queries from SSQLS structures. It’s
|
|
efficient, too: MySQL++ is smart enough to quote and escape
|
|
data only for those data types that actually require it.</para>
|
|
</sect3>
|
|
|
|
<sect3 id="ssqls-add-range">
|
|
<title>Inserting Many Rows</title>
|
|
|
|
<para>Inserting a single row is useful, to be sure,
|
|
but you might want to be able to insert many SSQLSes or
|
|
<classname>Row</classname> objects at once. MySQL++ knows
|
|
how to do that, too, sparing you the necessity of writing
|
|
the loop. Plus, MySQL++ uses an optimized implementation of
|
|
this algorithm, packing everything into a single SQL query,
|
|
eliminating the overhead of multiple calls between the
|
|
client and server. It’s just a different overload of
|
|
<methodname>insert()</methodname>, which accepts a pair of
|
|
iterators into an STL container, inserting every row in that
|
|
range:</para>
|
|
|
|
<programlisting>vector<stock> lots_of_stuff;
|
|
...populate the vector somehow...
|
|
query.insert(lots_of_stuff.begin(), lots_of_stuff.end()).execute();</programlisting>
|
|
|
|
<para>By the way, notice that you can chain
|
|
<classname>Query</classname> operations like in the last line
|
|
above, because its methods return <symbol>*this</symbol> where
|
|
that makes sense.</para>
|
|
</sect3>
|
|
|
|
<sect3 id="ssqls-insertfrom">
|
|
<title>Working Around MySQL’s Packet Size Limit</title>
|
|
|
|
<para>The two-iterator form of <methodname>insert()</methodname>
|
|
has an associated risk: MySQL has a limit on the size of the
|
|
SQL query it will process. The default limit is 1 MB. You
|
|
can raise the limit, but the reason the limit is configurable
|
|
is not to allow huge numbers of inserts in a single query. They
|
|
made the limit configurable because a single row might be bigger
|
|
than 1 MB, so the default would prevent you from inserting
|
|
anything at all. If you raise the limit simply to be able to
|
|
insert more rows at once, you’re courting disaster with
|
|
no compensating benefit: the more data you send at a time, the
|
|
greater the chance and cost of something going wrong. Worse,
|
|
this is pure risk, because by the time you hit 1 MB,
|
|
the per-packet overhead is such a small fraction of the data
|
|
being transferred that increasing the packet size buys you
|
|
essentially nothing.</para>
|
|
|
|
<para>Let’s say you have a <classname>vector</classname>
|
|
containing several megabytes of data; it will get even bigger
|
|
when expressed in SQL form, so there’s no way you can
|
|
insert it all in a single query without raising the MySQL packet
|
|
limit. One way to cope would be to write your own naïve
|
|
loop, inserting just one row at a time. This is slow, because
|
|
you’re paying the per-query cost for every row in the
|
|
container. Then you might realize that you could use the two
|
|
iterator form of <methodname>insert()</methodname>, passing
|
|
iterators expressing sub-ranges of the container instead of
|
|
trying to insert the whole container in one go. Now you’ve
|
|
just got to figure out how to calculate those sub-ranges to
|
|
get efficient operation without exceeding the packet size
|
|
limit.</para>
|
|
|
|
<para>MySQL++ already knows how to do that, too, with
|
|
<methodname>Query::insertfrom()</methodname>. We gave
|
|
it a different name instead of adding yet another
|
|
<methodname>insert()</methodname> overload because it
|
|
doesn’t merely build the <command>INSERT</command> query,
|
|
which you then <methodname>execute()</methodname>. It’s
|
|
more like <methodname>storein()</methodname>, in that it wraps
|
|
the entire operation up in a single call. This feature is
|
|
demonstrated in <filename>examples/ssqls6.cpp</filename>:</para>
|
|
|
|
<programlisting><xi:include href="ssqls6.txt" parse="text"
|
|
xmlns:xi="http://www.w3.org/2001/XInclude"/></programlisting>
|
|
|
|
<para>Most of the complexity in this example goes to
|
|
just reading in the data from a file; we have to get
|
|
our test data from somewhere. There are only two key
|
|
lines of code: create an insertion policy object, and
|
|
pass it along with an STL container full of row data to
|
|
<methodname>Query::insertfrom()</methodname>.</para>
|
|
|
|
<para>This policy object is the main thing that differentiates
|
|
<methodname>insertfrom()</methodname> from the two-iterator
|
|
form of <methodname>insert()</methodname>. It controls
|
|
how <methodname>insertfrom()</methodname> builds the query
|
|
strings, primarily controlling how large each query gets before
|
|
<methodname>insertfrom()</methodname> executes it and starts
|
|
building a new query. We designed it to use policy objects
|
|
because there is no single “right” choice for the
|
|
decisions it makes.</para>
|
|
|
|
<para>MySQL++ ships with three different insertion policy
|
|
classes, which should cover most situations.</para>
|
|
|
|
<para><classname>MaxPacketInsertPolicy</classname>, demonstrated
|
|
in the example above, does things the most obvious way: when
|
|
you create it, you pass the maximum packet size, which it uses
|
|
to prevent queries from going over the size limit. It builds
|
|
up a query string row by row, checking each time through the
|
|
loop whether adding another insert statement to the query string
|
|
would make the packet size go over the limit. When that happens,
|
|
or it gets to the end of the iteration range, it executes the
|
|
query and starts over if it’s not yet at the end. This
|
|
is robust, but it has a downside: it has to build each insert
|
|
query in advance of knowing that it can append it to the larger
|
|
query. Any time an insert query would push the packet over the
|
|
limit, it has to throw it away, causing the library to do more
|
|
work than is strictly necessary.</para>
|
|
|
|
<para>Imagine you’ve done some benchmarking and have found
|
|
that the point of diminishing returns is at about 20 KB per
|
|
query in your environment; beyond that point, the per-query
|
|
overhead ceases to be an issue. Let’s also say you
|
|
know for a fact that your largest row will always be less than
|
|
1 MB — less 20 KB — when expressed as a SQL
|
|
insert statement. In that case, you can use the more efficient
|
|
<classname>SizeThresholdInsertPolicy</classname>. It differs from
|
|
<classname>MaxPacketInsertPolicy</classname> in that it allows
|
|
<methodname>insertfrom()</methodname> to insert rows blindly into
|
|
the query string until the built query exceeds the threshold,
|
|
20 KB in this example. Then it ships the packet off, and if
|
|
successful, starts a new query. Thus, each query (except possibly
|
|
the last) will be at least 20 KB, exceeding that only by as
|
|
much as one row’s worth of data, minus one byte. This is
|
|
quite appropriate behavior when your rows are relatively small,
|
|
as is typical for tables not containing BLOB data. It is more
|
|
efficient than <classname>MaxPacketInsertPolicy</classname>
|
|
because it never has to throw away any SQL fragments.</para>
|
|
|
|
<para>The simplest policy object type is
|
|
<classname>RowCountInsertPolicy</classname>. This lets you simply
|
|
say how many rows at a time to insert into the database. This
|
|
works well when you have a good handle on how big each row
|
|
will be, so you can calculate in advance how many rows you
|
|
can insert at once without exceeding some given limit. Say
|
|
you know your rows can’t be any bigger than about
|
|
1 KB. If we stick with that 20 KB target, passing
|
|
<classname>RowCountInsertPolicy<>(20)</classname>
|
|
for the policy object would ensure we never exceed
|
|
the size threshold. Or, say that maximum size
|
|
value above is still true, but we also know the
|
|
average row size is only 200 bytes. You could pass
|
|
<classname>RowCountInsertPolicy<>(100)</classname> for
|
|
the policy, knowing that the average packet size will be around
|
|
20 KB, and the worst case packet size 100 KB, still
|
|
nowhere near the default 1 MB packet size limit. The code
|
|
for this policy is very simple, so it makes your program a little
|
|
smaller than if you used either of the above policies. Obviously
|
|
it’s a bad choice if you aren’t able to predict
|
|
the size of your rows accurately.</para>
|
|
|
|
<para>If one of the provided insert policy classes
|
|
doesn’t suit your needs, you can easily create
|
|
a custom one. Just study the implementation in
|
|
<filename>lib/insertpolicy.*</filename>.</para>
|
|
</sect3>
|
|
|
|
<sect3 id="ssqls-insertfrom-transactions">
|
|
<title>Interaction with Transactions</title>
|
|
|
|
<para>These policy classes are all templates, taking a parameter
|
|
that defaults to <ulink type="classref" url="Transaction"/>. This
|
|
means that, by default, <methodname>insertfrom()</methodname>
|
|
wraps the entire operation in a SQL transaction, so that if
|
|
any of the insertions fail, the database server rolls them all
|
|
back. This prevents an error in the middle of the operation
|
|
from leaving just part of the container’s data inserted
|
|
in the database, which you usually don’t want any more
|
|
than you’d want half a single row to be inserted.</para>
|
|
|
|
<para>There are good reasons why you might
|
|
not want this. Perhaps the best reason is if the
|
|
<methodname>insertfrom()</methodname> call is to be part
|
|
of a larger transaction. MySQL doesn’t support nested
|
|
transactions, so the <methodname>insertfrom()</methodname>
|
|
call will fail if it tries to start one of its own. You can
|
|
pass <classname>NoTransactions</classname> for the insert
|
|
policy’s template parameter to make it suppress the
|
|
transaction code.</para>
|
|
</sect3>
|
|
</sect2>
|
|
|
|
|
|
<sect2 id="ssqls-modifying">
|
|
<title>Modifying data</title>
|
|
|
|
<para>It almost as easy to modify data with SSQLS as to add it. This
|
|
is <filename>examples/ssqls3.cpp</filename>:</para>
|
|
|
|
<programlisting><xi:include href="ssqls3.txt" parse="text"
|
|
xmlns:xi="http://www.w3.org/2001/XInclude"/></programlisting>
|
|
|
|
<para>Don’t forget to run <filename>resetdb</filename> after
|
|
running the example.</para>
|
|
</sect2>
|
|
|
|
|
|
<sect2 id="ssqls-comparing">
|
|
<title>Storing SSQLSes in Associative Containers</title>
|
|
|
|
<para>One of the requirements of STL’s associative
|
|
containers on data stored in them is that the data type
|
|
has to be less-than comparable. That is, it has to have
|
|
an <function>operator <</function> defined.
|
|
SSQLS does optionally give you this, as demonstrated in
|
|
<filename>examples/ssqls4.cpp</filename>:</para>
|
|
|
|
<programlisting><xi:include href="ssqls4.txt" parse="text"
|
|
xmlns:xi="http://www.w3.org/2001/XInclude"/></programlisting>
|
|
|
|
<para>The <methodname>find()</methodname> call works because of
|
|
the way the SSQLS was declared. It’s properly covered
|
|
<link linkend="ssqls-compare-init">elsewhere</link>,
|
|
but suffice it to say, the “1” in the
|
|
declaration of <classname>stock</classname> <link
|
|
linkend="ssqls-retrieving">above</link> tells it that only the
|
|
first field needs to be checked in comparing two SSQLSes. In
|
|
database terms, this makes it the primary key. Therefore, when
|
|
searching for a match, our exemplar only had to have its first
|
|
field populated.</para>
|
|
</sect2>
|
|
|
|
|
|
<sect2 id="ssqls-table-name">
|
|
<title>Changing the Table Name</title>
|
|
|
|
<para>Another feature you might find a use for is changing the
|
|
table name MySQL++ uses to build queries involving SSQLSes. By
|
|
default, the database server table is assumed to have the same name
|
|
as the SSQLS structure type. But if this is inconvenient, you can
|
|
globally change the table name used in queries like this:</para>
|
|
|
|
<programlisting>
|
|
stock::table("MyStockData");</programlisting>
|
|
|
|
<para>It’s also possible to change the name of a table on
|
|
a per-instance basis:</para>
|
|
|
|
<programlisting>
|
|
stock s;
|
|
s.instance_table("AlternateTable");</programlisting>
|
|
|
|
<para>This is useful when you have an SSQLS definition that is
|
|
compatible with multiple tables, so the table name to use for
|
|
each instance is different. This feature saves you from having
|
|
to define a separate SSQLS for each table. It is also useful for
|
|
mapping a class hierarchy onto a set of table definitions. The
|
|
common SSQLS definition is the “superclass” for a
|
|
given set of tables.</para>
|
|
|
|
<para>Strictly speaking, you only need to use this feature in
|
|
multithreaded programs. Changing the static table name before
|
|
using each instance is safe if all changes happen within a single
|
|
thread. That said, it may still be convenient to change the name of
|
|
the table for an SSQLS instance in a single-threaded program if it
|
|
gets used for many operations over an extended span of code.</para>
|
|
</sect2>
|
|
|
|
|
|
<sect2 id="ssqls-in-header">
|
|
<title>Using an SSQLS in Multiple Modules</title>
|
|
|
|
<para>It’s convenient to define an SSQLS in a header file so
|
|
you can use it in multiple modules. You run into a bit of a
|
|
problem, though, because each SSQLS includes a few static data
|
|
members to hold information common to all structures of that
|
|
type. (The table name and the list of field names.) When you
|
|
<command>#include</command> that header in more than one module,
|
|
you get a multiply-defined symbol error at link time.</para>
|
|
|
|
<para>The way around this is to define the preprocessor macro
|
|
<varname>MYSQLPP_SSQLS_NO_STATICS</varname> in <emphasis>all but
|
|
one</emphasis> of the modules that use the header definining the
|
|
SSQLS. When this macro is defined, it suppresses the static data
|
|
members in any SSQLS defined thereafter.</para>
|
|
|
|
<para>Imagine we have a file <filename>my_ssqls.h</filename> which
|
|
includes a <function>sql_create_N</function> macro call to define an
|
|
SSQLS, and that that SSQLS is used in at least two modules. One
|
|
we’ll call <filename>foo.cpp</filename>, and we’ll say
|
|
it’s just a user of the SSQLS; it doesn’t
|
|
“own” it. Another of the modules,
|
|
<filename>my_ssqls.cpp</filename> uses the SSQLS more heavily, so
|
|
we’ve called it the owner of the SSQLS. If there aren’t
|
|
very many modules, this works nicely:</para>
|
|
|
|
<programlisting>
|
|
// File foo.cpp, which just uses the SSQLS, but doesn’t "own" it:
|
|
#define MYSQLPP_SSQLS_NO_STATICS
|
|
#include "my_ssqls.h"</programlisting>
|
|
|
|
<programlisting>
|
|
// File my_ssqls.cpp, which owns the SSQLS, so we just #include it directly
|
|
#include "my_ssqls.h"</programlisting>
|
|
|
|
<para>If there are many modules that need the SSQLS, adding all
|
|
those <command>#defines</command> can be a pain. In that case,
|
|
it’s easier if you flip the above pattern on its head:</para>
|
|
|
|
<programlisting>
|
|
// File my_ssqls.h:
|
|
#if !defined(EXPAND_MY_SSQLS_STATICS)
|
|
# define MYSQLPP_SSQLS_NO_STATICS
|
|
#endif
|
|
sql_create_X(Y, Z....) // the SSQLS definition</programlisting>
|
|
|
|
<programlisting>
|
|
// File foo.cpp, a mere user of the SSQLS:
|
|
#include "my_ssqls.h"</programlisting>
|
|
|
|
<programlisting>
|
|
// File my_ssqls.cpp, which owns the SSQLS:
|
|
#define EXPAND_MY_SSQLS_STATICS
|
|
#include "my_ssqls.h"</programlisting>
|
|
</sect2>
|
|
|
|
|
|
<sect2 id="ssqls-internals">
|
|
<title>Harnessing SSQLS Internals</title>
|
|
|
|
<para>The <symbol>sql_create</symbol> macros define several methods
|
|
for each SSQLS. These methods are mostly for use within the library,
|
|
but some of them are useful enough that you might want to harness
|
|
them for your own ends. Here is some pseudocode showing how the most
|
|
useful of these methods would be defined for the
|
|
<structname>stock</structname> structure used in all the
|
|
<filename>ssqls*.cpp</filename> examples:</para>
|
|
|
|
<programlisting>
|
|
// Basic form
|
|
template <class Manip>
|
|
stock_value_list<Manip> value_list(cchar *d = ",",
|
|
Manip m = mysqlpp::quote) const;
|
|
|
|
template <class Manip>
|
|
stock_field_list<Manip> field_list(cchar *d = ",",
|
|
Manip m = mysqlpp::do_nothing) const;
|
|
|
|
template <class Manip>
|
|
stock_equal_list<Manip> equal_list(cchar *d = ",",
|
|
cchar *e = " = ", Manip m = mysqlpp::quote) const;
|
|
|
|
|
|
// Boolean argument form
|
|
template <class Manip>
|
|
stock_cus_value_list<Manip> value_list([cchar *d, [Manip m,] ]
|
|
bool i1, bool i2 = false, ... , bool i5 = false) const;
|
|
|
|
// List form
|
|
template <class Manip>
|
|
stock_cus_value_list<Manip> value_list([cchar *d, [Manip m,] ]
|
|
stock_enum i1, stock_enum i2 = stock_NULL, ...,
|
|
stock_enum i5 = stock_NULL) const;
|
|
|
|
// Vector form
|
|
template <class Manip>
|
|
stock_cus_value_list<Manip> value_list([cchar *d, [Manip m,] ]
|
|
vector<bool> *i) const;
|
|
|
|
...Plus the obvious equivalents for field_list() and equal_list()</programlisting>
|
|
|
|
<para>Rather than try to learn what all of these methods do at
|
|
once, let’s ease into the subject. Consider this code:</para>
|
|
|
|
<programlisting>
|
|
stock s("Dinner Rolls", 75, 0.95, 0.97, sql_date("1998-05-25"));
|
|
cout << "Value list: " << s.value_list() << endl;
|
|
cout << "Field list: " << s.field_list() << endl;
|
|
cout << "Equal list: " << s.equal_list() << endl;</programlisting>
|
|
|
|
<para>That would produce something like:</para>
|
|
|
|
<programlisting>
|
|
Value list: 'Dinner Rolls’,75,0.95,0.97,'1998-05-25'
|
|
Field list: item,num,weight,price,sdate
|
|
Equal list: item = 'Dinner Rolls’,num = 75,weight = 0.95, price = 0.97,sdate = '1998-05-25'</programlisting>
|
|
|
|
<para>That is, a “value list” is a list of data member
|
|
values within a particular SSQLS instance, a “field
|
|
list” is a list of the fields (columns) within that SSQLS, and
|
|
an “equal list” is a list in the form of an SQL equals
|
|
clause.</para>
|
|
|
|
<para>Just knowing that much, it shouldn’t surprise you to
|
|
learn that <methodname>Query::insert()</methodname> is implemented
|
|
more or less like this:</para>
|
|
|
|
<programlisting>
|
|
*this << "INSERT INTO " << v.table() << " (" << v.field_list() <<
|
|
") VALUES (" << v.value_list() << ")";</programlisting>
|
|
|
|
<para>where ‘v’ is the SSQLS you’re asking the
|
|
Query object to insert into the database.</para>
|
|
|
|
<para>Now let’s look at a complete example, which uses one of
|
|
the more complicated forms of <methodname>equal_list()</methodname>.
|
|
This example builds a query with fewer hard-coded strings than the
|
|
most obvious technique requires, which makes it more robust in the
|
|
face of change. Here is
|
|
<filename>examples/ssqls5.cpp</filename>:</para>
|
|
|
|
<programlisting><xi:include href="ssqls5.txt" parse="text"
|
|
xmlns:xi="http://www.w3.org/2001/XInclude"/></programlisting>
|
|
|
|
<para>This example uses the list form of
|
|
<methodname>equal_list()</methodname>. The arguments
|
|
<varname>stock_weight</varname> and <varname>stock_price</varname>
|
|
are enum values equal to the position of these columns within the
|
|
<structname>stock</structname> table. <symbol>sql_create_#</symbol>
|
|
generates this enum for you automatically.</para>
|
|
|
|
<para>The boolean argument form of that
|
|
<methodname>equal_list()</methodname> call would look like
|
|
this:</para>
|
|
|
|
<programlisting>
|
|
query << "select * from stock where " <<
|
|
res[0].equal_list(" and ", false, false, true, true, false);</programlisting>
|
|
|
|
<para>It’s a little more verbose, as you can see. And if you want
|
|
to get really complicated, use the vector form:</para>
|
|
|
|
<programlisting>
|
|
vector<bool> v(5, false);
|
|
v[stock_weight] = true;
|
|
v[stock_price] = true;
|
|
query << "select * from stock where " <<
|
|
res[0].equal_list(" and ", v);</programlisting>
|
|
|
|
<para>This form makes the most sense if you are building many other
|
|
queries, and so can re-use that vector object.</para>
|
|
|
|
<para>Many of these methods accept manipulators and custom
|
|
delimiters. The defaults are suitable for building SQL queries, but
|
|
if you’re using these methods in a different context, you may
|
|
need to override these defaults. For instance, you could use these
|
|
methods to dump data to a text file using different delimiters and
|
|
quoting rules than SQL.</para>
|
|
|
|
<para>At this point, we’ve seen all the major aspects of the
|
|
SSQLS feature. The final sections of this chapter look at some of
|
|
the peripheral aspects.</para>
|
|
</sect2>
|
|
|
|
|
|
<sect2 id="ssqls-field-names">
|
|
<title>Having Different Field Names in C++ and SQL</title>
|
|
|
|
<para>There’s a more advanced SSQLS creation macro,
|
|
which all the others are built on top of. Currently, the only
|
|
feature it adds over what’s described above is that it
|
|
lets you name your SSQLS fields differently from the names
|
|
used by the database server. Perhaps you want to use <ulink
|
|
url="http://en.wikipedia.org/wiki/Hungarian_notation">Hungarian
|
|
notation</ulink> in your C++ program without changing the SQL
|
|
database schema:</para>
|
|
|
|
<programlisting>
|
|
sql_create_complete_5(stock, 1, 5,
|
|
mysqlpp::sql_char, m_sItem, "item",
|
|
mysqlpp::sql_bigint, m_nNum, "num",
|
|
mysqlpp::sql_double, m_fWeight, "weight",
|
|
mysqlpp::sql_decimal, m_fPrice, "price",
|
|
mysqlpp::sql_date, m_Date, "sdate")</programlisting>
|
|
|
|
<para>Note that you don’t have to use this mechanism if the
|
|
only difference in your SQL and C++ field names is case. SSQLS
|
|
field name lookups are case-insensitive as of MySQL++ 3.1. You can
|
|
see this in the examples: some parts of the code deliberately refer
|
|
to the <classname>stock.sdate</classname> sample table field as
|
|
<classname>stock.sDate</classname> to exercise this feature.</para>
|
|
</sect2>
|
|
|
|
|
|
<sect2 id="ssqls-pretty">
|
|
<title>Expanding SSQLS Macros</title>
|
|
|
|
<para>If you ever need to see the code that a given
|
|
SSQLS declaration expands out to, use the utility
|
|
<filename>doc/ssqls-pretty</filename>, like so:</para>
|
|
|
|
<programlisting>
|
|
doc/ssqls-pretty < myprog.cpp |less</programlisting>
|
|
|
|
<para>This Perl script locates the first SSQLS declaration in that
|
|
file, then uses the C++ preprocessor to expand that macro. (The
|
|
script assumes that your system’s preprocessor is called
|
|
<filename>cpp</filename>, and that its command line interface
|
|
follows Unix conventions.)</para>
|
|
|
|
<para>If you run it from the top MySQL++ directory, as shown above,
|
|
it will use the header files in the distribution’s
|
|
<filename>lib</filename> subdirectory. Otherwise, it assumes the
|
|
MySQL++ headers are in their default location,
|
|
<filename>/usr/include/mysql++</filename>. If you want to use
|
|
headers in some other location, you’ll need to change the
|
|
directory name in the <command>-I</command> flag at the top of the
|
|
script.</para>
|
|
</sect2>
|
|
|
|
|
|
<sect2 id="ssqls-customization">
|
|
<title>Customizing the SSQLS Mechanism</title>
|
|
|
|
<para>The SSQLS header <filename>ssqls.h</filename>
|
|
is automatically generated by the Perl script
|
|
<filename>ssqls.pl</filename>. Although it is possible to
|
|
change this script to get additional functionality, most of
|
|
the time it’s better to just derive a custom class from
|
|
the generated SSQLS to add functionality to it. (See the <link
|
|
linkend="ssqls-derivation">next section</link> to see how to do
|
|
this correctly.)</para>
|
|
|
|
<para>That said, <filename>ssqls.pl</filename> does have a few
|
|
configurables you might want to tweak.</para>
|
|
|
|
<para>The first configurable value sets the maximum number of
|
|
data members allowed in an SSQLS. This is discussed elsewhere,
|
|
in <xref linkend="max-fields"/>. Beware the warnings there about
|
|
increasing this value too much.</para>
|
|
|
|
<para>The second configurable is the default floating point
|
|
precision used for comparison. As described above (<xref
|
|
linkend="ssqls-compare-init"/>) SSQLSes can be compared for
|
|
equality. The only place this is tricky is with floating-point
|
|
numbers, since rounding errors can make two “equal”
|
|
values compare as distinct. This property of floating-point numbers
|
|
means we almost never want to do exact comparison. MySQL++ lets
|
|
you specify the precision you want it to use. If the difference
|
|
between two values is under a given threshold, MySQL++ considers
|
|
the values equal. The default threshold is 0.00001. This threshold
|
|
works well for “human” scale values, but because of the
|
|
way floating-point numbers work, it can be wildly inappropriate for
|
|
very large or very small quantities like those used in scientific
|
|
applications.</para>
|
|
|
|
<para>There are actually two ways to change this
|
|
threshold. If you need a different system-wide default,
|
|
edit <filename>ssqls.pl</filename> and change the
|
|
<varname>$fp_min_delta</varname> variable at the top of the file,
|
|
then rebuild <filename>ssqls.h</filename> as described below. If
|
|
you need different thresholds per file or per project, it’s
|
|
better to set the C macro <varname>MYSQLPP_FP_MIN_DELTA</varname>
|
|
instead. The Perl variable sets this macro’s
|
|
default; if you give a different value before #including
|
|
<filename>ssqls.h</filename>, it will use that instead.</para>
|
|
|
|
<para>To rebuild <filename>ssqls.h</filename> after changing
|
|
<filename>ssqls.pl</filename>, you’ll need a Perl
|
|
interpreter. The only modern Unixy system I’m aware
|
|
of where Perl isn’t installed by default is Cygwin, and
|
|
it’s just a <filename>setup.exe</filename> choice away
|
|
there. You’ll probably only have to download and install a
|
|
Perl interpreter if you’re on Windows and don’t want
|
|
to use Cygwin.</para>
|
|
|
|
<para>If you’re on a system that uses autoconf, building
|
|
MySQL++ automatically updates <filename>ssqls.h</filename>
|
|
any time <filename>ssqls.pl</filename> changes. Otherwise,
|
|
you’ll need to run the Perl interpreter by hand:</para>
|
|
|
|
<screen>c:\mysql++> cd lib
|
|
c:\lib> perl ssqls.pl</screen>
|
|
</sect2>
|
|
|
|
|
|
<sect2 id="ssqls-derivation">
|
|
<title>Deriving from an SSQLS</title>
|
|
|
|
<para>Specialized SQL Structures make good base
|
|
classes. They’re simple, and have few requirements on any
|
|
class that derives from them. There are some gotchas to look out
|
|
for, however.</para>
|
|
|
|
<para>Consider this:</para>
|
|
|
|
<programlisting>
|
|
sql_create_2(
|
|
Base, 1, 2,
|
|
mysqlpp::sql_varchar, a,
|
|
mysqlpp::sql_int, b
|
|
);
|
|
|
|
class Derived : public Base
|
|
{
|
|
public:
|
|
// constructor
|
|
Derived(mysqlpp::sql_varchar _a, mysqlpp::sql_int _b) :
|
|
Base(_a, _b)
|
|
{
|
|
}
|
|
|
|
// functionality added to the SSQLS through inheritance
|
|
bool do_something_interesting(int data);
|
|
};</programlisting>
|
|
|
|
<para>We’ve derived a class from an SSQLS in order to add
|
|
a method to it. Easy, right?</para>
|
|
|
|
<para>Sadly, too easy. The code has a rather large flaw which makes
|
|
our derived class unusable as an SSQLS. In C++, if a derived class
|
|
has a function of the same name as one in the base class, the
|
|
base class versions of that function are all hidden by those in
|
|
the derived class. This applies to constructors, too: an SSQLS
|
|
defines several constructors, but our derived class defines
|
|
only one, causing that one to hide all of the ones in the base
|
|
class. Many of the MySQL++ mechanisms that use SSQLSes rely on
|
|
having these contructors, so our <classname>Derived</classname>
|
|
above is-not-a <classname>Base</classname>, and so it isn’t
|
|
an SSQLS. If you try to use <classname>Derived</classname>
|
|
as an SSQLS, you’ll get compiler errors wherever MySQL++
|
|
tries to access one of these other constructors.</para>
|
|
|
|
<para>There’s another minor flaw, as well. Our lone constructor
|
|
above takes its parameters by value, but the corresponding
|
|
constructor in the SSQLS takes them by const reference. Our derived
|
|
class has technically hidden a fourth base class constructor this
|
|
way, but this particular case is more a matter of efficiency than
|
|
correctness. Code that needs the full-creation constructor will
|
|
still work with our code above, but passing stringish types like
|
|
<classname>sql_varchar</classname> by value instead of by const
|
|
reference is inefficient.</para>
|
|
|
|
<para>This is the corrected version of the above code:</para>
|
|
|
|
<programlisting>
|
|
sql_create_2(
|
|
Base, 1, 2,
|
|
mysqlpp::sql_varchar, a,
|
|
mysqlpp::sql_int, b
|
|
);
|
|
|
|
class Derived : public Base
|
|
{
|
|
public:
|
|
// default constructor<footnote><para>needed by mechanisms like <methodname>Query::storein()</methodname>; anything using an STL container, which usually require default ctors for contained data structures</para></footnote>
|
|
Derived() :
|
|
Base()
|
|
{
|
|
}
|
|
|
|
// for-comparison constructor<footnote><para>takes the <parameter>COMPCOUNT</parameter> subset of the SSQLS’s data members, used for making comparison exemplars, used with <methodname>Query::update()</methodname> and similar mechanisms; see <xref linkend="sql_create"/> for more on <parameter>COMPCOUNT</parameter></para></footnote>
|
|
Derived(const mysqlpp::sql_varchar& _a) :
|
|
Base(_a)
|
|
{
|
|
}
|
|
|
|
// full creation constructor
|
|
Derived(const mysqlpp::sql_varchar& _a, const mysqlpp::sql_int& _b) :
|
|
Base(_a, _b)
|
|
{
|
|
}
|
|
|
|
// population constructor<footnote><para>used in taking raw row data from a SQL result set and converting it to SSQLS form</para></footnote>
|
|
Derived(const mysqlpp::Row& row) :
|
|
Base(row)
|
|
{
|
|
}
|
|
|
|
// functionality added to the SSQLS through inheritance
|
|
bool do_something_interesting(int data);
|
|
};</programlisting>
|
|
|
|
<para>Now <classname>Derived</classname> is-an SSQLS.</para>
|
|
|
|
<para>You might wonder if you can use protected inheritance
|
|
above to redefine the SSQLS’s public interface. For
|
|
instance, OO purists might object to the public data members
|
|
in an SSQLS. You could encapsulate these public data members
|
|
in the derived class by using protected inheritance, exposing
|
|
access to the base class’s data members with public
|
|
accessor methods. The problem with this is that each SSQLS has
|
|
<emphasis>dozens</emphasis> of public member functions. These are
|
|
needed by MySQL++ internals, so unless you re-exposed all of them
|
|
as we did with the constructors above, you’d again have an
|
|
SSQLS derivative that is-not-an SSQLS. Simply put, only public
|
|
inheritance is practical with SSQLSes.</para>
|
|
</sect2>
|
|
|
|
|
|
<sect2 id="ssqls-blob">
|
|
<title>SSQLS and BLOB Columns</title>
|
|
|
|
<para>It takes special care to use SSQLS with BLOB columns.
|
|
It’s safest to declare the SSQLS field as of type
|
|
<classname>mysqlpp::sql_blob</classname>. This is currently a
|
|
typedef alias for <ulink type="classref"
|
|
url="String">String</ulink>, which is the form the data is in just
|
|
before the SSQLS mechanism populates the structure. Thus, when the
|
|
data is copied from the internal MySQL++ data structures into your
|
|
SSQLS, you get a direct copy of the <classname>String</classname>
|
|
object’s contents, without interference.</para>
|
|
|
|
<para>Because C++ strings handle binary data just fine, you might
|
|
think you can use <classname>std::string</classname> instead of
|
|
<classname>sql_blob</classname>, but the current design of
|
|
<classname>String</classname> converts to
|
|
|
|
|
|
<classname>std::string</classname> via a C string. As a result, the
|
|
BLOB data is truncated at the first embedded null character during
|
|
population of the SSQLS. There’s no way to fix that without
|
|
completely redesigning either <classname>String</classname> or the
|
|
SSQLS mechanism.</para>
|
|
|
|
<para>The <classname>sql_blob</classname> typedef may be changed to
|
|
alias a different type in the future, so using it instead of
|
|
<classname>String</classname> ensures that your code tracks these
|
|
library changes automatically. Besides,
|
|
<classname>String</classname> is only intended to be an internal
|
|
mechanism within MySQL++. The only reason the layering is so thin
|
|
here is because it’s the only way to prevent BLOB data from
|
|
being corrupted while avoiding that looming redesign effort.</para>
|
|
|
|
<para>You can see this technique in action in the
|
|
<filename>cgi_jpeg</filename> example:</para>
|
|
|
|
<programlisting><xi:include href="cgi_jpeg.txt" parse="text"
|
|
xmlns:xi="http://www.w3.org/2001/XInclude"/></programlisting>
|
|
</sect2>
|
|
|
|
|
|
<sect2 id="ssqls-vc2003">
|
|
<title>SSQLS and Visual C++ 2003</title>
|
|
|
|
<para>SSQLS works on all platforms supported by MySQL++ except for
|
|
Visual C++ 2003. (Because the rest of MySQL++ works just fine with
|
|
Visual C++ 2003, we haven’t removed this platform from the
|
|
supported list entirely.)</para>
|
|
|
|
<para>If you do need SSQLS and are currently on Visual C++ 2003, you
|
|
have these options:</para>
|
|
|
|
<orderedlist>
|
|
<listitem><para>The simplest option is to upgrade to a newer
|
|
version of Visual C++. The compiler limitations that break SSQLS
|
|
are all fixed in Visual C++ 2005 and newer. <ulink
|
|
url="http://www.microsoft.com/express/vc/">Visual C++
|
|
Express</ulink> is free and is apparently here to stay; coupled
|
|
with the free <ulink url="http://wxwidgets.org/">wxWidgets</ulink>
|
|
library, it lacks little compared to Visual C++ Professional. A
|
|
bonus of using wxWidgets is that it’s cross-platform and
|
|
better-supported than MFC.</para></listitem>
|
|
|
|
<listitem><para>If you can’t upgrade your compiler, you may
|
|
be able to downgrade to MySQL++ v2.<emphasis>x</emphasis>. The
|
|
SSQLS feature in these older versions worked with Visual C++ 2003,
|
|
but didn’t let you use a given SSQLS in more than one module
|
|
in a program. If you can live with that limitation and have a Perl
|
|
interpreter on your system, you can re-generate
|
|
<filename>lib/ssqls.h</filename> to remove the multiple-module
|
|
SSQLS support. To do this, you run the command <command>perl
|
|
ssqls.pl -v</command> from within MySQL++’s
|
|
<filename>lib</filename> subdirectory before you build and install
|
|
the library.</para></listitem>
|
|
|
|
<listitem><para>There’s <ulink
|
|
url="http://svn.gna.org/viewcvs/*checkout*/mysqlpp/trunk/Wishlist">a
|
|
plan</ulink> to replace the current SSQLS mechanism with an
|
|
entirely new code base. Although this is being done primarily
|
|
to get new features that are too difficult to add within the
|
|
current design, it also means we’ll have the chance to
|
|
test step-by-step along the way that we don’t reintroduce
|
|
code that Visual C++ 2003 doesn’t support. This may happen
|
|
without you doing anything, but if there’s someone on
|
|
the team who cares about this, that will naturally increase
|
|
the chances that it does happen.</para></listitem>
|
|
</orderedlist>
|
|
</sect2>
|
|
</sect1>
|