    <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:admin="http://webns.net/mvcb/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:content="http://purl.org/rss/1.0/modules/content/">
     <channel>
        <title>ACCU  :: Introduction to the Database Template Library</title>
        <link>https://members.accu.org/index.php/articles/445</link>
        <description>Professionalism in Programming</description>
        <dc:language>en-us</dc:language> 
        <dc:creator>Administrator</dc:creator> 
        <admin:generatorAgent rdf:resource="http://www.xaraya.org" /> 
        <admin:errorReportsTo rdf:resource="mailto:webeditor@accu.org" />
       <sy:updatePeriod>hourly</sy:updatePeriod>
       <sy:updateFrequency>1</sy:updateFrequency>
       <docs>http://backend.userland.com/rss</docs>




<div class="xar-mod-head"><span class="xar-mod-title">Programming Topics + Overload Journal #43 - Jun 2001</span></div>

<table border="0" cellpadding="1" cellspacing="0">
    <tbody>
    <tr>
        <td valign="top">
            Browse in :
       </td>
       <td valign="top">

                                            <a href="https://members.accu.org/index.php/articles/">All</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c13/">Topics</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c65/">Programming</a>
<br />

                                            <a href="https://members.accu.org/index.php/articles/">All</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c76/">Journals</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c78/">Overload</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c161/">43</a>
<br />

                                            <a href="https://members.accu.org/index.php/articles/c65-161/">Any of these categories</a>

                    -                        <a href="https://members.accu.org/index.php/articles/c65+161/">All of these categories</a>
<br />
</td>
   </tr>
   </tbody>
</table>




<div class="xar-error">
   <p>
 <strong>Note:</strong> when you create a new publication type,
the articles module will automatically use the templates
<em>user-display-[publicationtype].xt</em>
and <em>user-summary-[publicationtype].xt</em>.
If those templates do not exist when you try to preview or display a new article,
you'll get this warning :-)  Please place your own templates in themes/<em>yourtheme</em>/modules/articles . The templates will get the extension .xt there. </p>
</div>
<div class="xar-norm xar-standard-box-padding">
   <h1><strong>Title:</strong>&nbsp;Introduction to the Database Template Library</h1>
<p><strong>Author:</strong>&nbsp;</p>
<p>
<strong>Date:</strong> 26 June 2001 17:46:06 +01:00 or Tue, 26 June 2001 17:46:06 +01:00</p>
<p><strong>Summary:</strong>&nbsp;</p>
<p><strong>Body:</strong>&nbsp;<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e25" id="d0e25"></a></h2>
</div>
<p>The goal of this library is to make ODBC recordsets look just
like an STL container. As a user, you can move through our
containers using standard STL iterators; and if you <tt class=
"function">insert()</tt>, <tt class="function">erase()</tt> or
<tt class="function">replace()</tt> records in our containers
changes can be automatically committed to the database for you. The
library's compliance with the STL iterator and container standards
means you can plug our abstractions into a wide variety of STL
algorithms for data storage, searching and manipulation. In
addition, the C++ reflection mechanism used by our library to bind
to database tables allows us to add generic indexing and lookup
properties to our containers with no special code required from the
end-user. Because our code takes full advantage of the template
mechanism, it adds minimal overhead compared with using raw ODBC
calls to access a database.</p>
</div>
<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e38" id="d0e38"></a>Background</h2>
</div>
<p>Introduced in 1995, STL and templates represent one of the most
significant advances in the C++ language in the last decade. The
guiding force behind the power of the standard template library is
the notion of Generic Programming. At the heart of Generic
Programming is the idea of abstracting operations across as broad a
set of data types as possible to create algorithms that are as
generic as possible. This kind of design leads to abstractions that
are centred around a set of requirements on the data types
themselves. Examples in STL include notions such as iterators,
containers and set operations. We have taken these abstractions and
applied them to the problem of representing tables in a database.
In what follows, we will show how this simplifies the task of
manipulating data and provides instant access to a broad range of
algorithms that come with the standard template library.</p>
</div>
<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e43" id="d0e43"></a>A First
Example, Reading and Writing Records in a Table</h2>
</div>
<p>As our first example, we show what the code would look like to
open a table and read a set of rows from the database.</p>
<pre class="programlisting">
#include &quot;dtl.h&quot;
using namespace dtl;
// Connect to the database 
DBConnection::GetDefaultConnection().Connect(&quot;UID=example;PWD=example;DSN=example;&quot;);
// Create a container to hold records from a query.
// In this case, the query will be &quot;SELECT * FROM DB_EXAMPLE&quot;
DynamicDBView&lt;&gt; view(&quot;DB_EXAMPLE&quot;, &quot;*&quot;); 
// Read all rows from the database and send to cout
copy(view.begin(), view.end(), ostream_iterator&lt;variant_row&gt;(cout, &quot;\n&quot;));
</pre>
<p>The three steps shown above are:</p>
<div class="orderedlist">
<ol type="1">
<li>
<p>Connect to the database.</p>
</li>
<li>
<p>Create a <tt class="classname">DynamicDBView</tt> called
'<tt class="varname">view</tt>' which analyzes the database
structure at runtime and binds the query fields to a class called
'<tt class="varname">variant_row</tt>'.</p>
</li>
<li>
<p>Send all rows from the view to the standard output stream
'<tt class="classname">cout</tt>' using the STL copy algorithm.</p>
</li>
</ol>
</div>
<p>In addition to dynamic queries which examine the database at
runtime, we also provide templates that allow the user to bind
database tables directly to their own objects.</p>
</div>
<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e76" id="d0e76"></a>Mapping a Table
to a User Defined Object in Four Easy Steps</h2>
</div>
<div class="orderedlist">
<ol type="1">
<li>
<p>Define an object to hold the rows from your query.</p>
</li>
<li>
<p>Define an association between fields in your query and fields in
your object. This is what we call a 'BCA', which is short for Bind
Column Addresses. In the example below, this is done via the
functor &quot;BCAExample&quot;. The job of the BCA is to equate SQL fields
with object fields via the '==' operator which will then establish
ODBC bindings to move data to or from a user query.</p>
</li>
<li>
<p>Create a view to select records from. This view is built from
the template <tt class="classname">DBView</tt> and establishes
which table(s) you want to access, what fields you want to look at
(via the BCA), and an optional where clause to further limit the
set of records that you are working with. The <tt class=
"classname">DBView</tt> template forms a semi-Container in the STL
sense. [See <a href="http://www.sgi.com/tech/stl/Container.html"
target="_top">www.sgi.com/tech/stl/Container.html</a> for the
definition of an STL container, we call DBView a semi container
because it supports all standard container methods except
<tt class="methodname">size()</tt>, <tt class=
"methodname">max_size()</tt> and <tt class=
"methodname">empty()</tt>. We explain why these were left out by
design in the documentation for the DBView template.]</p>
</li>
<li>
<p>Use the <tt class="classname">DBView</tt> container to obtain an
iterator to SELECT, INSERT, UPDATE or DELETE records from your
view. These iterators may be used to either populate STL containers
or apply algorithms from the Standard Template library.</p>
</li>
</ol>
</div>
<p>In all the examples that follow we will assume that our database
contains a table called DB_EXAMPLE of the form:</p>
<pre class="screen">
SQL&gt; desc db_example;
Name                            Type
---------------                 ---------------
INT_VALUE                       INTEGER
STRING_VALUE                    VARCHAR
DOUBLE_VALUE                    FLOAT
EXAMPLE_LONG                    INTEGER
EXAMPLE_DATE                    DATE
</pre>
<pre class="programlisting">
// STEP 1 //// &quot;Example&quot; class to hold rows from our database table
class Example {
  public:                         // tablename.columnname:
  int exampleInt;                 // DB_EXAMPLE.INT_VALUE
  string exampleStr;              // DB_EXAMPLE.STRING_VALUE
  double exampleDouble;           // DB_EXAMPLE.DOUBLE_VALUE
  long exampleLong;               // DB_EXAMPLE.EXAMPLE_LONG
  TIMESTAMP_STRUCT exampleDate;   // DB_EXAMPLE.EXAMPLE_DATE
  Example(int exInt, const string &amp;exStr, double exDouble, long exLong,
      const TIMESTAMP_STRUCT &amp;exDate) : exampleInt(exInt), exampleStr(exStr), 
              exampleDouble(exDouble),exampleLong(exLong), exampleDate(exDate) { }
};

// STEP 2 ////
// Create an association between table columns and fields in our object
template&lt;&gt; class dtl::DefaultBCA&lt;Example&gt; {
public:
  void operator()(BoundIOs &amp;cols, Example &amp;rowbuf) {
    cols[&quot;INT_VALUE&quot;] == rowbuf.exampleInt;
    cols[&quot;STRING_VALUE&quot;] == rowbuf.exampleStr;
    cols[&quot;DOUBLE_VALUE&quot;] == rowbuf.exampleDouble;
    cols[&quot;EXAMPLE_LONG&quot;] == rowbuf.exampleLong;
    cols[&quot;EXAMPLE_DATE&quot;] == rowbuf.exampleDate;
  }
}

// STEP 3 &amp; 4
// Read the contents of the DB_EXAMPLE table and return a vector of the resulting rows
vector&lt;Example&gt; ReadData() {
  // Read the data
  vector&lt;Example&gt; results;     DBView&lt;Example&gt; view(&quot;DB_EXAMPLE&quot;);
  DBView&lt;Example&gt;::select_iterator read_it = view.begin();
  for( ; read_it != view.end(); read_it++) results.push_back(*read_it);
  return results;
}
</pre>
<p>At this point, it is worth discussing the types of iterators
exposed by <tt class="classname">DBView</tt>. The iterators that
<tt class="classname">DBView</tt> provides are either Input
iterators or Output iterators. In simple terms, an Input iterator
can read elements, but not write them. An Output iterator can write
elements, but not read them. These notions were first envisaged for
working with C++ input and output streams but they apply equally
well to reading and writing table data. Input and Output iterators
are also minimal types of iterators in that they don't guarantee
that table records will be read in any kind of specific or
consistent order and they don't provide for random access in the
sense that users cannot ask them to 'skip' ahead a given number of
records or go to a particular record number in the table. An exact
description of the functionality provided by Input and Output
iterators may be found at <a href=
"http://www.sgi.com/tech/stl/InputIterator.html" target=
"_top">http://www.sgi.com/tech/stl/InputIterator.html</a> and
<a href="http://www.sgi.com/tech/stl/OutputIterator.html" target=
"_top">http://www.sgi.com/tech/stl/OutputIterator.html</a>.</p>
<p>By restricting the iterators from <tt class=
"classname">DBView</tt> to be either input or output iterators, we
are able to provide database access with a minimum amount of code
overhead; thereby ensuring that read and write operations remain
efficient as compared with raw ODBC calls. The iterators provided
by <tt class="classname">DBView</tt> are as follows:</p>
<div class="variablelist">
<dl>
<dt><span class="term">Input Iterators</span></dt>
<dd>
<p>select_iterator, sql_iterator.</p>
</dd>
<dt><span class="term">Output Iterators:</span></dt>
<dd>
<p>insert_iterator, update_iterator, delete_iterator,
sql_iterator.</p>
</dd>
</dl>
</div>
<p>To illustrate the use of an output iterator we show how a vector
of rows would be inserted into a table.</p>
<pre class="programlisting">
// Using a DBView to insert rows into a database
// ... Class definitions for Example and BCAExample as per our ReadData example .....
// Specialization of DefaultInsValidate for Example
// This defines a business rule we wish to enforce for all Example objects before they 
// are allowed to be inserted into the database
template&lt;&gt; class dtl::DefaultInsValidate&lt;Example&gt; {
public:
  bool operator()(BoundIOs &amp;boundIOs, Example &amp;rowbuf) {  
    // data is valid if rowbuf.exampleStr is nonempty and rowbuf.exampleDouble is 
    // between 0 and 100 (like a percentage)
    return(rowbuf.exampleStr.length()&gt;0 &amp;&amp; rowbuf.exampleDouble &gt;= 0.0 
                           &amp;&amp; rowbuf.exampleLong &lt;= 100.0);
  }
};
// Insert rows from the vector&lt;Example&gt; parameter into the database
void WriteData(const vector&lt;Example&gt; &amp;examples) {
  DBView&lt;Example&gt; view(&quot;DB_EXAMPLE&quot;);
  // loop through vector and write Example objects to DB
  // write_it.GetCount() records written in loop
  DBView&lt;Example&gt;::insert_iterator write_it = view;
  for (vector&lt;Example&gt;::const_iterator ex_it = examples.begin(); 
                          ex_it != examples.end(); ex_it++, write_it++){
    *write_it = *ex_it;
    cout &lt;&lt; &quot;Writing element #&quot; &lt;&lt; write_it.GetCount() + 1&lt;&lt; endl;
  }
}
</pre>
<p>In <tt class="methodname">WriteData()</tt> we have used an
output iterator to insert records into our table in much the same
way that we used a read iterator to read records from a table. In
addition, this example introduces notion of client-side validation.
Often, when reading or writing records from a table we want to do
client side validation to make sure that the fields in a record are
not null or lie within an acceptable range of values. DBView
supports this through <tt class="function">SelValidate</tt> and
<tt class="function">InsValidate</tt> functions. The <tt class=
"function">SelValidate</tt> function validates records as they are
selected from the database. The <tt class=
"function">InsValidate</tt> function validates records as they are
inserted into the database. In the example above, we define a
<tt class="methodname">DefaultInsValidate</tt> function which
validates records before insertion to make sure the <tt class=
"classname">exampleStr</tt>, <tt class=
"classname">exampleDouble</tt> and <tt class=
"classname">exampleLong</tt> fields contain acceptable values
before allowing them to be inserted into the database.</p>
<p>In general, the constructor for DBView&lt;class DataObj, class
ParamObj = DefaultParamObj&lt;DataObj&gt;&gt; takes the form</p>
<pre class="programlisting">
DBView(const string &amp;tableList, const BCA &amp;bca_functor = DefaultBCA&lt;DataObj&gt;(),
       const string &amp;postfix = &quot;&quot;, const BPA &amp;bpa_functor = DefaultBPA&lt;ParamObj&gt;(),
       const SelVal sel_val = DefaultSelValidate&lt;DataObj&gt;(),
       const InsVal ins_val = DefaultInsValidate&lt;DataObj&gt;(),
       DBConnection &amp;connection = DBConnection::GetDefaultConnection())
</pre>
<p>which allows the user to define table names, field names, a
where clause, query parameters, a selection validation function, an
insert validation function and a database connection to use when
processing queries. If the user does not supply a validation
function then the default functions named <tt class=
"function">DefaultSelValidate</tt> and <tt class=
"function">DefaultInsValidate</tt> will be called. To see how the
postfix clause and parameters work we will next examine a more
complex case.</p>
</div>
<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e199" id="d0e199"></a>Tables R Us,
The IndexedDBView</h2>
</div>
<p>In practice, the most common operations performed on a set of
table records are: read the records into a container, search the
records by different key fields (i.e. indexes), and delete, insert
or update records in the container. For this reason, we have
developed a more advanced container for holding database tables.
This IndexedDBView container is a specialization of a Unique
Associative Container as defined by the standard template library
<a href=
"http://www.sgi.com/tech/stl/UniqueAssociativeContainer.html"
target=
"_top">www.sgi.com/tech/stl/UniqueAssociativeContainer.html</a>.</p>
<p>In addition to the base methods defined by the STL standard we
have coded features to make the container more copesetic with the
underlying rows that it contains. The main new features are the
easy creation of indexes into rows and synchronization capabilities
that can automatically propagate any changes back to the database.
This container comes at a price. It incurs more overhead than the
simple DBView and because it works at a higher level you lose a bit
of the fine-grained control that you get with simple iterators. To
explain, we begin with an example:</p>
<pre class="programlisting">
// &quot;Example&quot; class to hold rows from our database table
class Example {
  public:                                        // tablename.columnname:
  int exampleInt;                         // DB_EXAMPLE.INT_VALUE
  string exampleStr;                      // DB_EXAMPLE.STRING_VALUE
  double exampleDouble;                   // DB_EXAMPLE.DOUBLE_VALUE
  long exampleLong;                       // DB_EXAMPLE.EXAMPLE_LONG
  TIMESTAMP_STRUCT exampleDate;           // DB_EXAMPLE.EXAMPLE_DATE
  Example(int exInt, const string &amp;exStr, double exDouble, long exLong,
        const TIMESTAMP_STRUCT &amp;exDate) : exampleInt(exInt), exampleStr(exStr),
            exampleDouble(exDouble), exampleLong(exLong), exampleDate(exDate)  { }
};
// Parameter object to hold parameters for dynamic SQL query below 
class ParamObjExample {
public:
  int lowIntValue;     int highIntValue;
  string strValue;     TIMESTAMP_STRUCT dateValue;
};
// Create an association between table columns and fields in our object
class BCAExampleObj {
public:
  void operator()(BoundIOs &amp;boundIOs, Example &amp;rowbuf) {
     boundIOs[&quot;INT_VALUE&quot;]  == rowbuf.exampleInt;
     boundIOs[&quot;STRING_VALUE&quot;]  == rowbuf.exampleStr;
     boundIOs[&quot;DOUBLE_VALUE&quot;]  == rowbuf.exampleDouble;
     boundIOs[&quot;EXAMPLE_LONG&quot;]  == rowbuf.exampleLong;
     boundIOs[&quot;EXAMPLE_DATE&quot;]  == rowbuf.exampleDate;
  }
};
// Create an association between query parameters and fields in our parameters object
class BPAExampleObj {
public:
  void operator()(BoundIOs &amp;boundIOs, ParamObjExample &para;mObj) {
    boundIOs[0] == paramObj.lowIntValue;
    boundIOs[1] == paramObj.highIntValue;
    boundIOs[2] == paramObj.strValue;
    boundIOs[3] == paramObj.dateValue;
  }
};
// Set parameters function for Example ... used by IndexedDBView&lt;Example&gt; to set 
// dynamic query parameters Dynamic query parameters are indicated by (?) in our query 
// string for the IndexedDBView
void SetParamsExample(ParamObjExample &para;ms) {
  // set parameter values
  params.lowIntValue = 2;
  params.highIntValue = 8;
  params.strValue = &quot;Example&quot;;
  TIMESTAMP_STRUCT paramDate = {2000, 1, 1, 0, 0, 0, 0};
  params.dateValue = paramDate;
}
// Example of using an IndexDBView to read, insert and update records in a 
// container / database
void IndexedViewExample() {
  typedef DBView&lt;Example, ParamObjExample&gt; DBV;
  DBV view(&quot;DB_EXAMPLE&quot;,   BCAExampleObj(), 
    &quot;WHERE INT_VALUE BETWEEN (?) AND (?) OR &quot;
    &quot;STRING_VALUE = (?) OR EXAMPLE_DATE &lt;= (?) ORDER BY EXAMPLE_LONG&quot;,
    BPAExampleObj());
  IndexedDBView&lt;DBV&gt; indexed_view(view, &quot;UNIQUE PrimaryIndex; STRING_VALUE; AlternateIndex; EXAMPLE_LONG, EXAMPLE_DATE&quot;, 
    BOUND, USE_ALL_FIELDS, cb_ptr_fun(SetParamsExample));
  // Find the item where the STRING_VALUE matches the string &quot;Foozle&quot;
  IndexedDBView&lt;DBV&gt;::indexed_iterator idxview_it = indexed_view.find(string(&quot;Foozle&quot;));
  // Update the item with the key of &quot;Foozle&quot;, to read &quot;Fizzle&quot; instead
  if (idxview_it != indexed_view.end()) {
    Example replacement;
    replacement = *idxview_it;
    replacement.exampleStr = &quot;Fizzle&quot;;
    indexed_view.replace(idxview_it, replacement);
  }
  // Now find a second set of items using AlternateIndex
  // The STL convention for equal_range is to return a pair consisting of:  
  // 1. an iterator referring to the beginning of the list of found items
  // 2. an iterator pointing to the end of the list of found items. 
  // We will remove all items in this range.
  const TIMESTAMP_STRUCT date_criteria = {2000, 1, 1, 0, 0, 0, 0};
  long long_criteria = 33;
  IndexedDBView&lt;DBV&gt;::indexed_pair pr = indexed_view.equal_range_AK (&quot;AlternateIndex&quot;, long_criteria, date_criteria);
  idxview_it = pr.first;
  cout &lt;&lt; &quot;*** Size before erase calls: &quot; &lt;&lt; indexed_view.size() &lt;&lt; &quot; ***&quot; &lt;&lt; endl;
// Remove all items that match the criteria in our equal_range_AK lookup
  while (idxview_it != pr.second)   {
// As iterator is invalidated upon an erase(), use a temporary iterator to point to 
// DataObj to erase. Increment idxview_it before we erase so it will still be valid
// when we erase the DataObj.
    IndexedDBView&lt;DBV&gt;::indexed_iterator deleteMe = idxview_it;
    idxview_it++;
    indexed_view.erase(deleteMe);
  }
  cout &lt;&lt; &quot;*** Size after erase calls: &quot; &lt;&lt; indexed_view.size() &lt;&lt; &quot; ***&quot; &lt;&lt; endl;
// Finally, insert a new item into the container
  pair&lt;IndexedDBView&lt;DBV&gt;::iterator, bool&gt; ins_pr;
  ins_pr= indexed_view.insert(Example(459, &quot;Unique String #1&quot;, 3.4, 1, date_criteria));
  cout &lt;&lt; &quot;insertion succeded = &quot; &lt;&lt; (ins_pr.second == true ? &quot;true&quot;: &quot;false&quot;) &lt;&lt; endl;
}
</pre>
<p>To understand how <tt class="classname">IndexedDBView</tt> works
we begin with the constructor definition</p>
<pre class="programlisting">
IndexedDBView(DBView&lt;DataObj, ParamObj&gt; &amp;view, const string &amp;IndexNamesAndFields,
BoundMode bm = UNBOUND, KeyMode km = USE_ALL_FIELDS, SetParamsFn SetParams = NULL);
</pre>
<p>The first parameter here is a view object; this defines the SQL
Query that will be used to read and write records as described in
the previous two examples. The second parameter is <i class=
"parameter"><tt>IndexNamesAndFields</tt></i>; this defines indexes
on the rows in the container and we will examine it in more detail
shortly. The <i class="parameter"><tt>BoundMode</tt></i> and
<i class="parameter"><tt>KeyMode</tt></i> control whether or not
changes to the container data are synchronized with the database,
and if so what key fields are used for the synchronization. If
<tt class="literal">BoundMode = BOUND</tt>, then any changes to the
container are sent to the database. If <tt class=
"literal">BoundMode = UNBOUND</tt> then any changes to the
container will only apply locally. Finally, the SetParams function
allows the user to pass in an explicit function for setting
parameters in the where clause for the view if they so desire.</p>
<p>The <i class="parameter"><tt>IndexNamesAndFields</tt></i>
parameter is interesting. <i class=
"parameter"><tt>IndexNamesAndFields</tt></i> is used to
automatically create named indexes into our rows. In the above
example we have</p>
<pre class="programlisting">
IndexNamesAndFields = &quot;UNIQUE PrimaryIndex; STRING_VALUE; AlternateIndex; EXAMPLE_LONG, EXAMPLE_DATE&quot;;
</pre>
<p>What this does is create two indexes on the data that is read
into the container. The first index is designated to be a
<tt class="literal">UNIQUE</tt> with the name &quot;<tt class=
"literal">PrimaryIndex</tt>&quot; and is based on the field called
<tt class="literal">STRING_VALUE</tt>. Because this key is
designated as unique this forms a constraint on the container
whereby every entry for (<tt class="literal">STRING_VALUE</tt>)
must be unique in order for the associated row to be added to the
table. The second index is created with the name &quot;<tt class=
"literal">AlternateIndex</tt>&quot; and is based on the fields
<tt class="literal">EXAMPLE_LONG</tt> and <tt class=
"literal">EXAMPLE_DATE</tt>. <tt class=
"literal">AlternateIndex</tt> is not designated to be unique here
and is created only to provide a way to quickly look up rows based
on the values in the <tt class="literal">EXAMPLE_LONG</tt> and
<tt class="literal">EXAMPLE_DATE</tt> fields.</p>
<p>Why do we care about this? Doesn't the normal STL associative
container already provide lookup and retrieval using keys? Well,
the normal associative containers in STL have two limitations that
we found quite tedious to work with in practice. The first
limitation is that if you want an STL container to provide lookup
capabilities then you need to manually write comparison functions
for each class and index that you want to use. As the number of
tables and indexes grow, manually maintaining these comparison
functions gets to be a bit tedious. The <i class=
"parameter"><tt>IndexNamesAndFields</tt></i> syntax can
automatically create indexes given a list of field names. The
internal comparison functions that are created are slightly slower
than using hand made comparison operators, but, the performance
difference is not that great and we feel that the loss is more than
made up for by the increased ease of use and maintainability. The
second limitation is that the STL containers only support a single
index on the data. We found this rather confining since we often
want to be able to search the same set of rows quickly using
various subsets of the row fields. For this reason, <i class=
"parameter"><tt>IndexNamesAndFields</tt></i> allows you to create
multiple indexes on the rows in your container. To see how these
features are used to search based on the <tt class=
"literal">PrimaryIndex</tt> and <tt class=
"literal">AlternateIndex</tt> we examine the following lines from
the above example:</p>
<pre class="programlisting">
idxview_it = indexed_view.find(string(&quot;Foozle&quot;));
pr = indexed_view.equal_range_AK(&quot;IndexLongDate&quot;, long_criteria, date_criteria);
</pre>
<p>Standard STL containers provide a <tt class=
"methodname">find</tt> method to locate objects in the container.
This method is typically defined as follows:</p>
<pre class="programlisting">
container&lt; DataObj &gt;::find
const_iterator find(const DataObj &amp; key) const;
</pre>
<p>The <tt class="methodname">find</tt> member function returns an
iterator that designates the earliest element in the controlled
sequence whose sort key equals key. If no such element exists, the
iterator equals.</p>
<p>In the <tt class="classname">IndexedDBView</tt> container, we
overload the <tt class="methodname">find()</tt> function with
multiple versions:</p>
<pre class="programlisting">
template&lt;class DataField&gt; indexed_iterator find(const DataField &amp;df1); // One field find
template&lt;class DataField1, class DataField2&gt; indexed_iterator find(const DataField1 &amp;df1, const DataField2 &amp;df2); // Two field find
template&lt;class DataField1, class DataField2, class DataField3&gt; indexed_iterator find(const DataField1 &amp;df1, const DataField2 &amp;df2, const DataField &amp;df3);
// Four field find, five field find, etc.
indexed_iterator find(const DataObj &amp;key) // Standard find 
</pre>
<p>As per the standard, we provide a <tt class=
"methodname">find(DataObj)</tt> method to locate elements in the
container. Our default <tt class="methodname">find</tt> method uses
the first index passed into the <tt class=
"classname">IndexDBView</tt> constructor to locate objects, and
will return a match based only on the fields in that index. In
addition to the default find method, we have added overloaded
versions of the <tt class="methodname">find</tt> method to perform
a find using only the fields needed by the index. For example, in
the case of <tt class=
"literal">indexed_view.find(string(&quot;Foozle&quot;))</tt> , the <tt class=
"methodname">find()</tt> function resolves to <tt class=
"methodname">find&lt;DataField&gt; (const DataField &amp;df1)</tt>.
This is useful, because it allows us to execute a find by directly
supplying the criteria fields that we care about rather than having
to manually initialize an entire data object just to perform a find
operation.</p>
<p>In addition to <tt class="methodname">find()</tt> operations
using the primary index, we can also find an object based upon any
of the indexes named in the constructor for <tt class=
"classname">IndexDBView</tt>. This is done via the <tt class=
"methodname">find_AK</tt> function. For example, we could say
<tt class="literal">indexed_view.find_AK( &quot;AlternateIndex&quot;,
long_criteria, date_criteria)</tt> , which would find the first
element that matches the criteria provided by <i class=
"parameter"><tt>long_criteria</tt></i> and <i class=
"parameter"><tt>date_criteria</tt></i> using the fields named in
the &quot;<tt class="literal">AlternateIndex</tt>&quot; to determine if we
have a match.</p>
<p>Finally, you will notice that the above code has calls to
<tt class="methodname">insert()</tt>, <tt class=
"methodname">replace()</tt> and <tt class="methodname">erase()</tt>
methods for <tt class="classname">IndexedDBView</tt>. One major
difference between the <tt class="classname">IndexedDBView</tt>
container and a standard container is that any changes made to the
items in our container can be automatically propagated back to the
database. If we construct the container to initialize in what we
call &quot;<tt class="literal">Bound</tt>&quot; mode then any changes made to
the container are also sent to the database. In our example, when
we call the <tt class="methodname">erase()</tt> method, this
removes the item in the container and also deletes the underlying
record in the database. Similarly, <tt class=
"methodname">insert()</tt> and <tt class=
"methodname">replace()</tt> will modify both container and the
database.</p>
</div>
<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e390" id="d0e390"></a>When you
don't know what you need, dynamic queries</h2>
</div>
<p>The queries shown above assume that you know exactly what your
target table looks like and are able to define static objects to go
against known fields in these tables. In practice, you often end up
in the situation where you have a query with an unknown number of
columns with unknown types and you want to bind a dynamic object to
this query. To solve this problem, our library has two additional
containers called <tt class="classname">DynamicDBView</tt> and
<tt class="classname">DynamicIndexedDBView</tt> which perform
binding to a variant row class. This variant row class allows for
an arbitrary number of fields, with each field being of an
arbitrary type<sup>[<a name="d0e401" href="#ftn.d0e401" id=
"d0e401">1</a>]</sup>. The type and number of fields in variant row
are determined at run-time by querying the underlying database to
find the number of fields in the query and the type of each field
that is to be returned. To illustrate, we present an example:</p>
<pre class="programlisting">
// Using a DynamicDBView to read rows from the database.
// Read the contents of a table and print the resulting rows
void SimpleDynamicRead() {
  // Our query will be &quot;SELECT * FROM DB_EXAMPLE&quot;
  DynamicDBView&lt;&gt; view(&quot;DB_EXAMPLE&quot;, &quot;*&quot;);
// NOTE: We need to construct r from the view itself since we don't know what fields 
// the table will contain. We therefore make a call to the DataObj() function to have 
// the table return us a template row with the correct number of fields and field types.
// We use this construction since we can't be guaranteed that the table is non-empty &amp; 
// we want to still display column names in this case.
  variant_row s(view.GetDataObj());
  // Print out the column names
  vector&lt;string&gt; colNames = s.GetNames();
  for(vector&lt;string&gt;::iterator name_it =colNames.begin(); name_it != colNames.end(); name_it++){
    cout &lt;&lt; (*name_it) &lt;&lt; &quot; &quot;;
  }
  cout &lt;&lt; endl;
  // Print out all rows and columns from our query
  DynamicDBView&lt;&gt;::select_iterator print_it = view.begin();
  for (print_it = view.begin(); print_it != view.end(); print_it++) {
    variant_row r = *print_it;
    for (size_t i = 0; i &lt; r.size(); i++) cout &lt;&lt; r[i] &lt;&lt; &quot; &quot;;
    cout &lt;&lt; endl;
  }
}
</pre>
<p>Unlike the DBView code presented above, in <tt class=
"classname">DynamicDBView</tt> there is no notion of a BCA to bind
records to a particular class since the assumption is that
<tt class="classname">DynamicDBView</tt> will always bind to a
<tt class="classname">variant_row</tt> object. Therefore, the
<tt class="classname">DynamicDBView</tt> is constructed by
specifying a table name and a list of fields to select from the
table (in this case we use &quot;*&quot; to specify all fields in the table).
When we go to retrieve rows from our table, the row iterator
returns <tt class="classname">variant_row</tt> objects.
Essentially, <tt class="classname">variant_row</tt> is an array of
varying types designed to hold the fields from our query.
<tt class="classname">variant_row</tt> is constructed when the
query is first executed, at which time the view interrogates the
database in order to find out the number and types of fields that
will be returned. Here we use three methods from <tt class=
"classname">variant_row</tt> in order to display our results.</p>
<p>First, we call <tt class="methodname">GetNames()</tt> in order
to obtain a vector of the field names in our query. To retrieve the
field names, we must first initialize a <tt class=
"classname">variant_row</tt> object from the view:</p>
<pre class="programlisting">
variant_row s(view.GetDataObj());
</pre>
<p>It is crucial that we initialize all <tt class=
"classname">variant_row</tt> objects that we want to use from our
view class. This is because a single <tt class=
"classname">variant_row</tt> object is shared by all dynamic views
and therefore they have to initialize their particular version at
runtime to tell <tt class="classname">variant_row</tt> what fields
it will need to hold from the query. The second method that we use
from <tt class="classname">variant_row</tt> is the <tt class=
"methodname">size()</tt> method. This returns the number of fields
in our row. Finally, we access individual fields within a row via
the <tt class="methodname">[]</tt> operator. The <tt class=
"methodname">[]</tt> operator returns a <tt class=
"classname">variant_field</tt> object that we can use to read,
write or print individual fields. Individual fields may be
specified by either field name or field number. To illustrate, we
continue with a second example that uses <tt class=
"classname">DynamicIndexedDBView</tt>. What this example does is to
repeat the <tt class="classname">IndexedViewExample</tt> code shown
above; but it uses a <tt class="classname">variant_row</tt> object
to do all its work rather than a specialized Example class.</p>
<pre class="programlisting">
// Using a DynamicIndexedDBView to read, update and insert records in a database.
// Dynamic IndexedDBView example
// ... classes as in IndexedDBView example  ....
void DynamicIndexedViewExample() {
 DynamicDBView&lt;ParamObjExample&gt; dynamic_view(&quot;DB_EXAMPLE&quot;,
   &quot;INT_VALUE, STRING_VALUE, DOUBLE_VALUE, EXAMPLE_LONG,  EXAMPLE_DATE&quot;,
   &quot;WHERE INT_VALUE BETWEEN (?) AND (?) OR &quot;
    &quot;STRING_VALUE = (?) OR EXAMPLE_DATE &lt;= (?) ORDER BY EXAMPLE_LONG&quot;,
    BPAExampleObj());
 DynamicIndexedDBView&lt; DynamicDBView&lt;ParamObjExample&gt; &gt;  
   indexed_view(dynamic_view, 
         &quot;UNIQUE PrimaryIndex; STRING_VALUE;&quot;
         &quot;IndexLongDate; EXAMPLE_LONG, EXAMPLE_DATE&quot;,
   BOUND, USE_ALL_FIELDS, cb_ptr_fun(SetParamsExample));
// Find the item where the STRING_VALUE matches the string  &quot;Foozle&quot;
 DynamicIndexedDBView&lt; DynamicDBView&lt;ParamObjExample&gt; &gt;::indexed_iterator idxview_it = 
                         indexed_view.find(string(&quot;Foozle&quot;));
 // Update the item with the key of &quot;Foozle&quot;, to read  &quot;Fizzle&quot; instead
 if (idxview_it != indexed_view.end()) {
  variant_row replacement;
  replacement = *idxview_it;
  replacement[&quot;STRING_VALUE&quot;] =   string(&quot;Fizzle&quot;);
  indexed_view.replace(idxview_it, replacement);
 }
 // Now find a second set of items using AlternateIndex
 // The STL convention for equal_range is to return a pair  consisting of: 
 // 1. an iterator referring to the beginning of the list of found  items
 // 2. an iterator pointing to the end of the list of found items. 
 // We will remove all items in this range.
 const TIMESTAMP_STRUCT date_criteria = {2000, 1, 1, 0, 0, 0, 0};
 long long_criteria = 33;
 DynamicIndexedDBView&lt; DynamicDBView&lt;ParamObjExample&gt; &gt;::indexed_pair 
   pr = indexed_view.equal_range_AK(&quot;IndexLongDate&quot;, long_criteria, date_criteria);
 idxview_it = pr.first;
 cout &lt;&lt; &quot;*** Size before erase calls: &quot; &lt;&lt; indexed_view.size() &lt;&lt; &quot; ***&quot; &lt;&lt; endl; 
 // Remove all rows that matched the criteria in our equal_range_AK lookup 
 while (idxview_it !=&quot;pr.second)&quot; { 
   // as iterator is invalidated upon an erase(), use a temporary iterator 
   // to point to DataObj to erase 
   // increment idxview_it before we erase so it will still be valid 
   // when we erase the DataObj 
   DynamicIndexedDBView&lt;DynamicDBView&lt;ParamObjExample&gt; &gt;::indexed_iterator deleteMe=&quot;idxview_it;&quot; 
   idxview_it++; 
   indexed_view.erase(deleteMe); 
 } 
 cout &lt;&lt; &quot;*** Size after erase calls: &quot; &lt;&lt; indexed_view.size() &lt;&lt; &quot; ***&quot; &lt;&lt; endl; 
 // Finally, insert a new item into the container 
 pair&lt;DynamicIndexedDBView&lt; DynamicDBView&lt;ParamObjExample&gt; &gt;::iterator, bool&gt; ins_pr; 
 variant_row r(indexed_view.GetDataObj()); 
 r[&quot;INT_VALUE&quot;]=459; 
 r[&quot;STRING_VALUE&quot;]=string(&quot;Unique String #1&quot;); 
 r[&quot;DOUBLE_VALUE&quot;]=3.5; 
 r[&quot;EXAMPLE_LONG&quot;]=1; 
 r[&quot;EXAMPLE_DATE&quot;]=date_criteria; 
 ins_pr=indexed_view.insert(r);
 cout &lt;&lt; &quot;insertion succeded=&quot; &lt;&lt;  (ins_pr.second == true ? &quot; true&quot;: &quot; false&quot;) &lt;&lt; endl; 
}
</pre></div>
<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e483" id=
"d0e483"></a>Conclusion</h2>
</div>
<p>In the foregoing article we presented an STL centric paradigm
for reading, writing and updating table data from an ODBC data
source. The library we presented is centred around the notion of
representing database table operations via standard STL iterators
and containers. Our presentation was at an overview level for these
iterators and containers; full technical details have been left to
the reference documentation that we provide with the library. The
advantage of following the STL iterator and container paradigm is
that we are able to plug our database abstractions into a wide
variety of STL algorithms for data storage, indexing and
manipulation. In addition, the C++ reflection mechanism that we
introduced to bind iterators to database tables allows us to add
powerful automatic indexing and lookup features to our container
representations.</p>
<p>The DTL library is available for download from this URL:
<a href="http://www.geocities.com/corwinjoy/dtl/" target=
"_top">http://www.geocities.com/corwinjoy/dtl/</a></p>
</div>
<div class="footnotes"><br>
<hr class="c2" width="100">
<div class="footnote">
<p><sup>[<a name="ftn.d0e401" href="#d0e401" id=
"ftn.d0e401">1</a>]</sup> Our variant row type uses a template
mechanism to be able to hold values of common database types. It is
loosely based on the variant_t class proposed by Fernando Cacciola.
See F. Cacciola (2000). &quot;An Improved Variant Type Based on Member
Templates,&quot; <i class="citetitle">C++ Users Journal</i> Oct 2000, p.
10.</p>
</div>
</div>
</p>
<p><strong>Notes:</strong>&nbsp;</p>
<p><em>More fields may be available via dynamicdata ..</em></p>
</div>
</channel>
</rss>
