DB Interface Module User’s Guide : PART IV Using Open SQL : Chapter 15 Using the Open SQL Classes : The Data Callback Classes : Implementing a Custom Callback Class
Implementing a Custom Callback Class
To use the data callback feature:
1. Create a derived class from one of the three callback interface classes.
2. Implement the necessary callback methods in the derived class.
3. Provide an instance of the derived class to an RWDBOSql instance.
4. Execute the RWDBOSql.
To fetch or insert character data, derive from the base class RWDBCharCallback; for binary data, derive from RWDBBinaryCallback; and for UTF-16 data, derive from RWDBUChar16Callback. Note that only LOB data types are supported. Then define both of the pure virtual methods onFetch() and onSend(), and if needed, getLength().
Implementing onFetch()
To fetch data, implement the onFetch() method. This discussion assumes the use of RWDBBinaryCallback. The other two callback classes have slightly different signatures.
 
virtual bool onFetch(size_t rownum, const void* theData,
size_t byteLength, RWDBNullIndicator ni, bool& lastPiece);
The parameters are:
rownum
The row in the current rowset this data is from (0-based indexing)

This parameter is always zero if the database vendor does not support array binding, and therefore operates on just one row at a time. If your vendor does support array binding, you can use multirow operations by specifying a value greater than one for the entries parameter in the constructor for the callback class you are using. The entries value determines the size of the rowsets returned by each call to the database.

For example, if the result of a SELECT statement consists of 10 rows of data, the size of the result set is 10. If the callbacks are created with an entries value of 3, the first rowset will have 3 rows from the result set, 0 - 2, the second rowset will have rows 3 - 5, the third rowset will have rows 6 - 8 and the last rowset just a single row, row 9. However, rownum indicates the row in the current rowset, not the row in the result set. So, for example, when the first rowset is returned, rownum can have a value from 0 to 2. When the second rowset is returned, with rows 3 - 5, rownum will still have a value from 0 to 2, because it indicates the row in the current rowset, not the row as it was in the complete result set.
theData
A pointer to an array containing the piece of data currently being processed

Note: if the ni parameter is true, theData is undefined.
byteLength
The length in bytes of the data in theData

Note: if the ni parameter is true, theData is undefined.
ni
A boolean value indicating whether the value returned by the call to onFetch() is null

This value is true for null values, otherwise false.
lastPiece
A boolean value indicating whether this is the last piece of data

This parameter is true if this is last piece of data for the current row, otherwise false. If there might be reason to stop processing a row in the result set before all the data has been fetched, this parameter can be set to true to discontinue processing for the current row and move onto the next one.
Implementing onSend()
To insert data, implement the onSend() method:
 
virtual bool onSend(size_t rownum, void* theData,
size_t& byteLength, RWDBNullIndicator& ni, bool& lastPiece);
The parameters are:
rownum
The row for which data is needed (0-based indexing)

This parameter indicates the row of data being sent to the server.

For example, consider this code:

RWDBOSql inserter("insert into strings values(:ph0)");
MyDataCallback dataBuffer(1000);
inserter << dataBuffer;
inserter.execute(aConn);

When RWDBOSql::execute(..) is called, 1000 strings are inserted into the database by repeatedly calling the onSend() method associated with dataBuffer. For each row, the onSend() method is invoked as many times as needed until all the data is sent. The rownum indicates the row in the 1000 rows for which data is needed. Therefore, rownum will have values from 0 to 999.
theData
A pointer to the array where the onSend() method should write the data to be sent

This location is passed to onSend() by the access module, which has allocated a location to receive the data.
byteLength
The number of bytes in theData to be sent to the server

Initially this parameter holds the maximum size of the data that can be sent to the server, as specified by the piece size value for the access module. The onSend() method should set this variable to the actual size of the data being sent to the server.
ni
A boolean reference value for indicating nulls

This boolean value is set to true to indicate that a null value is being sent to the database, otherwise false.

If this parameter is set to true, the parameters theData, byteLength, and lastPiece are irrelevant.
lastPiece
A boolean reference value for indicating that this is the last piece of data for the current value

When the last piece of data for the current row's value is written by onSend() to the array theData, lastPiece must be set to true.
The onSend() method must return true to continue the operation. If it returns false, or throws an exception, the operation is terminated and no subsequent calls are made to onSend().
Implementing getLength()
Some databases require knowing at the beginning of the operation the total length of the data to be inserted into a row. Check the access module documentation for your database to determine whether this information is required. If so, you must implement the getLength() method:
 
size_t getLength(size_t rownum) const;
The rownum parameter has the same meaning as it has for onSend() above. This method is called just before the initial call to onSend() for each row to get the total length of the data being sent for the row.
Note that if the getLength() method is needed but not overridden from the base class, the inserting of data will fail.