dtl


BoundIO and BoundIOs

Category: utilities Component type: type

Description

A BoundIO is an object used to hold an association between a field in a query and a field in a user defined data object. The BoundIO class is held in a private member of the DBView template called the BoundIOs object which is an STL map<string, BoundIO> that holds a set of BoundIO objects that are mapped by SQL field name. As an end user, the four places you will encouter a BoundIO are when writing BCA , BPA, InsVal or SelVal functions.

Definition

Defined in the BoundIO.h header file.

Refinement of

None.

Associated types

None.

Example 1:

Functor to bind SQL columns to a data object
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
};
class BCAExampleObj
{
public:
	void operator()(BoundIOs &boundIOs, Example &rowbuf)
    	{
	   boundIOs["INT_VALUE"] 	== rowbuf.exampleInt;
	   boundIOs["STRING_VALUE"]	== rowbuf.exampleStr;
	   boundIOs["DOUBLE_VALUE"]	== rowbuf.exampleDouble;
	   boundIOs["EXAMPLE_LONG"]	== rowbuf.exampleLong;
	   boundIOs["EXAMPLE_DATE"]	== rowbuf.exampleDate;
	}
};

 

Example 2:

//BPA Functor to bind SQL parameters to a data object

// "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)
	{ }

};

// Create an association between table columns and fields in our object
class BCAExampleObj
{
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;
	}
}

class ExampleParamObj
{
    public:
       	int lowIntValue;
	int highIntValue;
	string strValue;
	TIMESTAMP_STRUCT dateValue;
};

class BPAParamObj
{
public:
	void operator()(BoundIOs &boundIOs, ExampleParamObj &paramObj)
	{
	  boundIOs[0] == paramObj.lowIntValue;
	  boundIOs[1] == paramObj.highIntValue;
	  boundIOs[2] == paramObj.strValue;
	  boundIOs[3] == paramObj.dateValue;
	}

};

// read some Example objects from the database and return a vector of
// the results, use BPA to set join parameters
vector<Example> ReadData()
{
	vector<Example> results;

	// construct view
	
	DBView<Example, ExampleParamObj>
		view("DB_EXAMPLE", BCAExampleObj(),
		"WHERE INT_VALUE BETWEEN (?) AND (?) AND "
		"STRING_VALUE = (?) OR EXAMPLE_DATE < (?) ORDER BY EXAMPLE_LONG",
		BPAParamObj());

	// loop through query results and add them to our vector
	// in this loop, read_it.GetLastCount() records read from DB

	DBView<Example, ExampleParamObj>::select_iterator read_it = view.begin();

	// set parameter values for the WHERE clause in our SQL query
	read_it.Params().lowIntValue = 2;
	read_it.Params().highIntValue = 8;
	read_it.Params().strValue = "Example";
	
	TIMESTAMP_STRUCT paramDate = {2000, 1, 1, 0, 0, 0, 0};
	read_it.Params().dateValue = paramDate;

	for ( ; read_it != view.end();  read_it++)
	{
		cout << "Reading element #" << read_it.GetLastCount() << endl;
		results.push_back(*read_it);

		cout << "read_it->exampleInt = " << read_it->exampleInt << endl;
		cout << "read_it->exampleStr = " << read_it->exampleStr << endl;
		
	}
	
	return results;
}

 

Example 3:

//Default SelVal function to make sure fields in a row selected from the database are valid

// Default select validation behavior ... data is valid if and only if
// there are no columns which are null.
// If there are other checks you wish to make, put them in
// your own SelVal functor.
// You can also specialize this template if you wish to have different default behavior
// for your data class.
template<class DataObj> class DefaultSelValidate {
public:
bool operator()(BoundIOs &boundIOs,DataObj &rowbuf)
{
	for (BoundIOs::iterator b_it = boundIOs.begin();
				b_it != boundIOs.end(); b_it++)
	{
		BoundIO &boundIO = (*b_it).second;
		if (boundIO.IsColumn() && boundIO.IsNull())
			return false;  // found null column ... data is invalid
	}

	return true;	// no nulls found ... data is OK
}
};

Public base classes: BoundIO

None.

Public base classes: BoundIOs

map<string, BoundIO>

Notation:

X A type that is a model of BoundIO
a Object of type X

Expression semantics: BoundIO

Name Expression Precondition Semantics Postcondition
Default constructor
X a()
  Construct an empty BoundIO  
Copy constructor
X a(constX &b)
  Copy construct a BoundIO.  
Assignment operator
X& operator=(const X&b)
  Assignment copy  
Binding operator template<class T> void operator==(T &memberToBind)   Create an association between SQL column names / parameter numbers and object field names. This is done by inserting elements into the BoundIOs map. The syntax for inserting elements into the BoundIOs map is to call BoundIOs["SQL FIELD NAME" / SQL Parameter Number] == RowObject.FieldName. This invokes the == operator for the BoundIO class. We use the == syntax here as a mnemonic to remind end users that this is a two way association. Once the association is created, values can either be read from the database to the field, or written from the field to the database. Internally, this syntax does two things. First, the operator==() analyzes the RowObject.FieldName parameter to determine what type of field we are binding to and the memory address of the given field name (there are some restrictions! see [1]). Based on this information, operator==(), determines the SQL_TYPE, the SQL_C_TYPE, the data size, and the address to pass to SQLBindCol() or SQLBindParam(). All of this information gets stored in what is known as a BoundIO object. Finally, the resulting BoundIO object is inserted into the BoundIOs map using the BoundIOs::operator[]() described below.  
Column Indicator
bool IsColumn()
  Returns true if this BoundIO represents a SQL field.  
Parameter Indicator
bool IsParam()
  Returns true if this BoundIO represents a SQL parameter.  
NULL data Indicator
bool IsNull()
  Returns true if value held in this field by the current rowbuf object holds NULL data. Typically this is used by the SelVal function.  

Expression semantics: BoundIOs

As per STL map but with the following modifications:

Name Expression Precondition Semantics Postcondition
Parameter operator
BoundIO &operator[](unsigned int paramNum)
  This is used to insert/find a BoundIO in the BoundIOs map using a stringified version of the number as the key. If this operator does not find an existing BoundIO with the given key, it creates a new BoundIO object that is marked as a SQL parameter with the name given by the key string. This new BoundIO object is then inserted into the BoundIOs list. SQL parameters are represented in the SQL string passed to a DBView by a '(?)'.  
Column operator
BoundIO &operator[](const string &colName)
  This is used to insert/find a BoundIO in the BoundIOs map using a the given string as the key. If this operator does not find an existing BoundIO with the given key, it creates a new BoundIO object that is marked as a SQL field with the name given by the key string. This new BoundIO object is then inserted into the BoundIOs list.  

Notes

[1] If you are only using this BCA/BPA with a DBView template then the field you are binding to can be either statically or dynamically allocated by the DataObj, as long as the object has a correct copy and assigment operator for copying the field. If you want to use an IndexedDBView with your BCA, then the field must be a statically allocated member of the DataObj. The reason for this is that IndexedDBView computes a relative fieldoffset via &(rowbuf.Field) - &rowbuf that it uses for comparison functions for automatic indexing and update routines in the IndexedDBView. This kind of offset logic is only valid if the field is not dynamically allocated via operator new() or malloc(). So, if you want to use IndexedDBView we recommend that your fields be statically allocated members. (In fact, there is a complicated way around this restriction if you allocate your fields as a contiguous block. You can check out DynamicRowBCA in DynaDBView.h and the data_ptr() function that we define for the variant_row class in variant_row.h for an example of how we worked around this to accomodate dynamically sized rows. The result is not for the faint of heart.) Here is an example of what we are talking about:

#define DYNAMIC_SIZE 100 
class BCADynamicObj;
class Dynamic {
	char **szDynamicString;

public:

	Dynamic() {
		szDynamicString = (char **)malloc(DYNAMIC_SIZE);
	}

	Dynamic(const Dynamic &other) {
		szDynamicString = (char **)malloc(DYNAMIC_SIZE);	
		memcpy(szDynamicString, other.szDynamicString, DYNAMIC_SIZE);
	}

	const Dynamic & operator=(const Dynamic &other) {
		if (this != &other) {
			if (szDynamicString == NULL)
				szDynamicString = (char **)malloc(DYNAMIC_SIZE);	
			memcpy(szDynamicString, other.szDynamicString, DYNAMIC_SIZE);
		}

	}
	

	~Dynamic() {
		if (szDynamicString != NULL)
			free(szDynamicString);
	}

	friend class BCADynamicObj;
};

// This BCA will work with DBView but not IndexedDBView because szDynamicString is dynamic
class BCADynamicObj
{
public:
	void operator()(BoundIOs &boundIOs, Dynamic &rowbuf)
    	{
	   boundIOs["STRING_VALUE"] == *(rowbuf.szDynamicString);
	}
};

See also

BPA, BCA, InsVal, SelVal, DBView, IndexedDBView


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