dtl


DBView<DataObj, ParamObj>::select_iterator

Category: iterators Component type: type

Description

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.

Definition

Defined in the select_iterator.h header file.

Example:

Accessing a Table in Four Easy Steps:


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.

Template parameters

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> 

Model of

Input Iterator

Type requirements

DataObj and ParamObj must each fulfill the following requirements:.

Public base classes

DB_iterator<DataObj, ParamObj>, iterator<input_iterator_tag, DataObj>

Members

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).

New members

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.

Notes

[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:

See also

DB_iterator, Output Iterator, Input Iterator.


[DTL Home]

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.