dtl
![]() |
![]() |
Category: iterators | Component type: type |
DBView<DataObj, ParamObj>::select_iterator is an Input Iterator that performs the reading of objects of type DataObj from a particular DBView (and thus the database). The select_iterator generates the following SQL statement to read records from the database: "SELECT " + "<field1_fromBCA>, <field2_fromBCA>, ... " + "FROM " + "<tablename1_from_view>, <tablename2_from_view>, ... " + posfix_clause_from_view. Note that all of the restrictions of an Input Iterator must be obeyed, including the restrictions on the ordering of operator* and operator++ operations.
Defined in the select_iterator.h header file.
1. Define an object to hold the rows from your query.
2. 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 "BCAExample". 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.
3. Create a view to select records from. This view is built from the template DBView 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 DBView template forms a semi-Container in the STL sense.1.
4. Use the DBView 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.
In all the examples that follow we will assume that our database contains a table called DB_EXAMPLE of the form
SQL> desc db_example;
Name Type
------------------------------- --------
INT_VALUE INTEGER
STRING_VALUE VARCHAR
DOUBLE_VALUE FLOAT
EXAMPLE_LONG INTEGER
EXAMPLE_DATE DATE
// STEP 1 ////
// "Example" 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 &exStr, double exDouble, long exLong,
const TIMESTAMP_STRUCT &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<> class dtl::DefaultBCA<Example>
{
public:
void operator()(BoundIOs &cols, Example &rowbuf)
{
cols["INT_VALUE"] == rowbuf.exampleInt;
cols["STRING_VALUE"] == rowbuf.exampleStr;
cols["DOUBLE_VALUE"] == rowbuf.exampleDouble;
cols["EXAMPLE_LONG"] == rowbuf.exampleLong;
cols["EXAMPLE_DATE"] == rowbuf.exampleDate;
}
}
// STEP 3 & 4
// Read the contents of the DB_EXAMPLE table and return a vector of the
// resulting rows
vector<Example> ReadData() {
// Read the data
vector<Example> results;
DBView<Example> view("DB_EXAMPLE");
DBView<Example>::select_iterator read_it = view.begin();
for ( ; read_it != view.end(); read_it++)
{
results.push_back(*read_it);
}
return results;
}
1 See http://www.sgi.com/tech/stl/Container.html for the definition of an STL container, we
call DBView a semi container because it supports all
standard container methods except size(), max_size() and
empty(). We explain why these were left out by design in the
documentation for the DBView template.
Parameter | Description | Default |
---|---|---|
DataObj | The type of object that will be written to the DBView. This object will be bound through use of the BCA to the appropriate columns in the database. The set of value types of an DBView::insert_iterator consists of a single type, DataObj. | |
ParamObj | The type of object that will be used to specify the postfix parameters to the DBView. | DefaultParamObj<DataObj> |
DataObj and ParamObj must each fulfill the following requirements:.
DB_iterator<DataObj, ParamObj>, iterator<input_iterator_tag, DataObj>
Member | Where defined | Description |
---|---|---|
DBView::select_iterator() | select_iterator | Default constructor. |
DBView::select_iterator(DBView<DataObj, ParamObj> &view) | select_iterator | See below. |
DBView::select_iterator(const DBView::select_iterator&) | Input Iterator | The copy constructor. See Note [2]. |
DBView::select_iterator& operator=(const DBView select_iterator&) | Input Iterator | The assignment operator |
const DataObj &operator*() | Input Iterator | Dereferencing operator. Returns the DataObj pointed to in the DBView. This operator forbids assigning to the iterator's DataObj. |
CountedPtr<DataObj> operator->() | Input Iterator, DB_iterator | Dereferencing operator. Returns a pointer to the DataObj read from the DBView. |
DBView::select_iterator& operator++() | Input Iterator | Preincrement. Reads a DataObj to the DBView. See Note [1]. |
const DBView::select_iterator operator++(int) | Input Iterator | Postincrement Reads a DataObj to the DBView. See Note [1]. |
friend bool operator==(const select_iterator &i1, const select_iterator &i2) | Input Iterator | Returns whether the two iterators are equal, that is, do they refer to the same DataObj? See Note [3]. |
friend bool operator!=(const select_iterator &i1, const select_iterator &i2) | Input Iterator | Returns whether the two iterators are not equal. Equivalent to !(i1 == i2). |
These members are not defined in the Output Iterator requirements or in DB_iterator<DataObj, ParamObj>, but are specific to DBView::select_iterator.
Function | Description |
---|---|
DBView::select_iterator(DBView<DataObj, ParamObj> &view) | Creates an select_iterator which refers to view. See Note [2]. |
void swap(DBView::select_iterator &other) | Swap *this with other. |
[1] This is the operation that actually reads the DataObj from the database via the DBView. Each DBView::select_iterator internally owns a DBStmt object which is allocated and prepared when the underlying ODBC statement handle is first needed and not before. The handle is not opened until absolutely needed in order to make copying and assigning these iterators an inexpensive operation. The DBStmt is executed on each call to operator++(), whether the prefix or postfix version.
[2] There is also a variant of this constructor which takes a second argument specifying a dummy whether the iterator is at the beginning or end of the DBView. It is used internally and should never be called by the end user.
[3] According to the standard, certain invariants must be maintained for an Input Iterator. The select_iterator implementation both adheres to the following invariants as well as taking advantage of them (assume x and y are both select_iterator's):
Warning: The following explanation is not for the faint of heart!
In a view over a database, the records can appear in a different order. Thus, there is no true notion of equality for iterators over a view. However, the standard requires the Identity invariant to be true. The Assignable and Equality Comparable invariant must also hold for select_iterator's according to the standard. Thus, we had to find a way to give these iterators a notion of equality. To simulate equality, we added a reference counting scheme for all DataObj's. Upon the creation of a new iterator using the form of the constructor which takes a DBView reference, the DBView creates a new DataObj and the iterator gets a pointer back to that new DataObj. Then, when an iterator copy constructs off of or gets assigned to by another iterator, this DataObj pointer gets copied to the new iterator and the iterator gets the view to increment the reference count for that DataObj. The operator==() compares two select_iterator's simply by comparing their DataObj pointers. The select_iterator's are equal if and only if these pointers are equal. When the new select_iterator first increments (that is, it fetches the next DataObj from the database), the iterator has the view create a fresh DataObj (the view decrements the old DataObj's reference count of course by one) for it to point to so that the iterators are conceptually no longer equal. This form of reference counting and comparison preserve both invariants. The Increment Equality Not Guaranteed invariant helped us out a bit. As x == y is not required to yield ++x == ++y, we were able to use the above trick of throwing away the old DataObj reference and just grabbing a new one upon the new iterator's first call to operator++().
[4] This function is a bit peculiar for select_iterator's. For the notion of a DBView's begin() and end() iterators, begin() must point to the first DataObj in the view and end() to one past the last DataObj in the view. To provide this conceptual behavior, the dereferencing operators must be able to grab the first record from the database if the iterator hasn't done so already. So the ReadData() function in the above example code will say its reading elements 0, 2, 3, 4, etc., rather than the 1, 2, 3, 4, etc., you would expect. This behavior occurs as the following happens:
DB_iterator,
Output
Iterator, Input
Iterator.
Copyright © 2001, Michael Gradman and Corwin Joy.
Permission to use, copy, modify, distribute and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appears in all copies and that both that copyright notice and this permission notice appear in supporting documentation. Corwin Joy and Michael Gradman make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty.