7.4 The Value Layer
The value layer consists of the data and variables you pass by value when writing to the value-based classes of the DB Interface Module. Performance features such as inserter and reader caching make these classes both efficient and easy to use. (See Chapter 9, “Caching.”) Applications using these classes can move data back and forth safely and efficiently between the value layer and the variables in the application. Typically, this is done with the insertion << and extraction >> operators of the various classes. For example, if you want to insert some integers into a table by value, you would place them into an RWDBInserter:
 
int i = 255;
RWDBInserter insert = myTable.inserter();
insert << 1234 << i;
To read a string and a float from a table, you would extract them from an RWDBReader:
 
RWCString s; float f;
RWDBReader reader = anotherTable.reader();
while (reader()) {
reader >> s >> f;
// process s and f
}
Code in the value layer of the DB Interface Module data model is responsible for converting the literal 1234 and the int i into RWDBTBuffer<T> instances for internal storage, and for converting other RWDBTBuffer<T> instances into application variables. Although this example is simple, it illustrates several features provided by the value layer:
Use of general purpose data types. The example code above uses general purpose data types, ints, floats, and RWCStrings. The database classes RWDBInserter and RWDBReader are used exclusively for data transport; no special types or structures are needed to store the data itself.
Type safety. Database programming is notorious for its lack of type safety. This is mainly because it is impossible to know at compile time which data types belong in which database objects at runtime. By using the DB Interface Module, you can attain runtime type safety; the data model ensures that only type-safe conversions are attempted.
Memory management. Notice that there is no explicit binding of program variables in the example above. In fact, there are no pointers at all. This eliminates a very common cause of error. Explicit variable binding is available when extra control over memory management is desired; however, it should only be used when necessary.
Robustness. There isn't any error checking in the example above. For now, simply note that an application can freely continue in the face of errors, without worrying about crashing because of a lack of error checking. (See Chapter 6, “The Error Model.”)
The structured data types used in the value layer include:
*RWCString, RWBasicUString, RWWString, RWDateTime, RWDecimalPortable (from the Essential Tools Module)
*RWDBMBString, RWDBDateTime (deprecated), RWDBDuration, RWDBBlob, and RWDBValue (from the DB Interface Module)
Classes RWBasicUString, RWWString, and RWDBMBString are briefly described in Chapter 13, “Internationalization.” The other classes are briefly described in the following sections.
7.4.1 RWCString
Class RWCString can be found in the Essential Tools Module. It is an industry standard for string manipulation, providing string processing features that are just as efficient as those in C, but far less prone to errors. Its features include memory management, collation, substrings, pattern matching, regular expressions, I/O, tokenizing, and support for multibyte strings.
Class RWCString has member functions to read, compare, store, restore, concatenate, prefix, and append RWCStrings and char*'s. Operators allow access to individual characters, with or without bounds checking.
For a full discussion of the features of this class, please see the Essential Tools Module User's Guide and the SourcePro C++ API Reference Guide.
7.4.2 RWDateTime
Class RWDateTime, also contained in the Essential Tools Module, is the primary class used for dates and times in the DB Interface Module. This class serves as a compact representation for calendar calculations. It shields you from many of the details of programming with time elements, like leap years, and performs conversions to and from conventional calendar formats.
SourcePro DB compatibility is provided to RWDateTime instances that are initialized to either RWDateTime::null or a valid date. For a full discussion of the many features of this class, please see the Essential Tools Module User's Guide and the SourcePro C++ API Reference Guide.
Class RWDateTime should be used for dates and time in SourcePro DB. Class RWDBDateTime is deprecated.
When RWDateTime is used to either write or extract data to or from a database, by default it uses the local time zone represented by RWZone::local(). Methods on RWDBDatabase and RWDBConnection allow you to specify a different time zone.
 
const RWZone* RWDBDatabase::timeZone(const RWZone* zone)
const RWZone& RWDBDatabase::timeZone() const
The first method sets the time zone on the RWDBDatabase instance, and all RWDBConnection instances produced by it after the time zone is set. The RWDBDatabase instance retains a reference to the time zone, hence it is the application's responsibility to ensure that the time zone has a lifetime greater than the RWDBDatabase instance or any object produced from it. The second method allows you to determine the current setting for this RWDBDatabase.
 
const RWZone* RWDBConnection::timeZone(const RWZone* zone)
const RWZone& RWDBConnection::timeZone() const
The first method sets the time zone for the specific RWDBConnection instance. The RWDBConnection instance retains a reference to the time zone, hence it is the application's responsibility to ensure that the time zone has a lifetime greater than the RWDBConnection instance or any object using it. The second method allows you to determine the current setting for this RWDBConnection.
If the database client you are using provides a time zone setting on its connections, the above methods do not set or get those time zone settings. If you wish to get or set the time zone setting on the database client connection, you can do so by executing the database-specific SQL for getting or setting the time zone using RWDBConnection::executeSql().
7.4.2.1 Example
The following example shows how RWDateTime performs date calculations. This code prints out the date January 6, 1990, then calculates and prints the date of the previous Sunday, using the global locale:
 
#include <rw/db/db.h>
#include <rw/rstream.h>
 
int
main() {
RWDateTime dd(1990U, 1, 6);
 
cout << dd.asString() << ", a " << dd.weekDayName() << endl;
 
RWDateTime prev = dd.previous("Sunday");
 
cout << "The previous Sunday is: " << prev.asString() << endl;
return 0;
}
Program output:
01/06/90 00:00:00.000, a Saturday
The previous Sunday is: 12/31/89 00:00:00.000
7.4.2.2 Constructors
An RWDateTime may be constructed in many ways. For example, you can:
*Construct an RWDateTime with the current date:
 
RWDateTime dt(RWDateTime::setCurrentTime);
*Construct an RWDateTime for a given year, month, and day:
 
RWDateTime d1(1924U, 1, 24); // Jan.24, 1924
*Construct an RWDateTime from an RWDate, supplying hours, minutes, seconds, and milliseconds:
 
RWDate d(10, 3, 90); // Mar. 10, 1990
RWDateTime dt(d, 1, 30, 30, 100) // Mar. 10, 1990 1:30:30.100
 
In the first example, we use the enumerated constructor to construct RWDateTime with current value. If you are constructing an array and must fill in today’s date, you must assign the dates explicitly (see the Essential Tools documentation for more details).
There are many other constructors, including those that use RWDate or RWTime in various ways. There are accessors for each component of an RWDateTime (years, months, days, hours, and so on). RWDateTime provides powerful localization features which may be accessed and changed via the RWDateTime interface. Member operators and functions allow a complete range of arithmetic manipulations on date and time values.
Complete information on the capabilities of RWDateTime can be found in the Essential Tools Module User's Guide and the SourcePro C++ API Reference Guide.
7.4.3 RWDBDateTime
The deprecated class RWDBDateTime used to be the primary class for representing and manipulating dates and time in the DB Interface Module. It is now reimplemented on class RWDateTime of the Essential Tools Module of SourcePro Core.
RWDateTime has the following advantages over RWDBDateTime:
*RWDBDateTime uses RWDateTime internally. Direct use of RWDateTime may result in increased application performance.
*RWDateTime handles an operation’s manipulation of time operations more intuitively.
RWDBDateTime is still included in the SourcePro DB API, and can be used interchangeably with RWDateTime. Code written with RWDBDateTime need not be rewritten. However, Rogue Wave recommends developing new applications with class RWDateTime. For information on using RWDateTime in SourcePro DB, please see Section 7.4.2 in this manual, and the entry for RWDateTime in the SourcePro C++ API Reference Guide.
To continue using RWDBDateTime, see the entry on RWDBDateTime in the SourcePro C++ API Reference Guide. If you are a previous user of RWDBDateTime, also see the following sections on changes to the RWDBDateTime API beginning with SourcePro DB 5.2.
7.4.3.1 Conversions between RWDBDateTime and RWDateTime
RWDBDateTime is now completely compatible with RWDateTime. Conversions are provided between the two classes, as in the following example:
 
RWDBDateTime datetime = RWDateTime(rwint64(0));
It is also possible to convert any RWDateTime instance to an RWDBDateTime instance:
 
RWDateTime datetime;
RWDBDateTime dbdatetime(datetime);
The way that RWDBDateTime stores dates was changed in SourcePro DB 5.2. It now carries the same limits and restrictions as RWDateTime. Please see the entry for class RWDateTime in the Essential Tools Module User's Guide.
7.4.3.2 Persistence
The RWDBDateTime methods saveOn() and restoreFrom() now save and restore the date in a different format than before SourcePro DB 5.2. The new format is more efficient in both time and space. To provide a migration path, the static method persistence() was provided to allow a change in the runtime behavior of the methods saveOn() and restoreFrom().
To read dates saved in the previous format, make the following call:
 
RWDBDateTime::persistence(RWDBDateTime::Legacy);
This sets the persistence model of the class to the legacy format. By default, dates are stored in a new format that is compatible with RWDateTime. To change the persistence model back to the default format, make the following call:
 
RWDBDateTime::persistence(RWDBDateTime::Default);
To determine the current persistence model, make the following call:
 
RWDBDateTime::Persistence persistence = RWDBDateTime::persistence();
After this call, the variable persistence can be compared to RWDBDateTime::Default and RWDBDateTime::Legacy.
As mentioned previously, the new persistence model is completely compatible with RWDateTime. For this reason, an instance of RWDBDateTime saved to a file can be read back as RWDateTime and vice versa.
7.4.3.3 64-bit Variables
RWDBDateTime is valid for thousands of years into the future. This means that several methods take 64-bit values as parameters in order to cover the full range of valid values. The methods that now take 64-bit values are: addMilliseconds(), addSeconds(), addMinutes(), and addHours(). Additionally, the method seconds() returns a 64-bit value. For example:
 
rwint64 num = RWDateTime::millisecsInDay * 100000;
RWDBDateTime datetime;
datetime.addMilliseconds(num);
rwint64 timeSince1901 = datetime.seconds();
For detailed information on functions that take 64-bit parameters, please see the entry for RWDBDateTime (deprecated) in the SourcePro C++ API Reference Guide.
7.4.3.4 Optional RWZone Parameter
Beginning with SourcePro DB 5.2, several RWDBDateTime (deprecated) methods now have a new optional parameter of type RWZone that allows a zone to be specified. For more information on these methods, please see the entries for RWDBDateTime and RWZone in the SourcePro C++ API Reference Guide.
7.4.3.5 Current Time
Beginning with SourcePro DB 5.2, when an instance of RWDBDateTime is set to the current time using the method now() or through the use of the default constructor, the milliseconds portion is no longer set to zero. The milliseconds portion now reflects the actual time.
7.4.4 RWDBDuration
Class RWDBDuration represents a time span, stored in a double as a number of seconds. All reasonable arithmetic operations involving time spans are supported, including addition and subtraction, multiplication by a constant, incrementation by seconds, minutes, hours, and so forth.
RWDBDuration also supports arithmetic operations involving the imprecise quantities months and years, whose exact number of days vary. The DB Interface Module supports these operations using the following conversions:
Table 12 – Time conversions in RWDBDuration
Time Span
const double RWDBDuration::DUR_MILLISECONDS_PER_SEC = ((double)1000.0);
const double RWDBDuration::DUR_SECONDS_PER_MIN = ((double)60.0);
const double RWDBDuration::DUR_SECONDS_PER_HR = ((double)3600.0);
const double RWDBDuration::DUR_SECONDS_PER_DAY = ((double)86400.0);
const double RWDBDuration::DUR_SECONDS_PER_WEEK = ((double)604800.0);
const double RWDBDuration::DUR_SECONDS_PER_RWMTH = ((double)2419200.0);
const double RWDBDuration::DUR_SECONDS_PER_RWYR = ((double)29030400.0);
From the table, we see that adding one month to an RWDBDuration adds four weeks' worth of seconds to the duration, regardless of the number of weeks in any particular month.
As a reminder of this interpretation, the methods for accessing years and months are named asRWMonths() and asRWYears(). Applications can apply specialized arithmetic for durations that span more than one month or year by using the arithmetic operators for doubles.
7.4.5 RWDBBlob
Class RWDBBlob provides memory management services and access to a binary large object (blob) with arbitrary data and length. Class RWDBBlob is derived from RWCollectable, so all features of a collectable are supported, including persistance. Class RWDBBlob does not provide any semantic interpretation of its data; it is designed as a base class that applications can specialize to provide application-specific semantics.
In the following example, we illustrate how a hypothetical structure, struct SOUND, might be transferred between a database and program variables. To use this code, the <ver> placeholder in any library names would need to be replaced with the actual digits representing the library version number.
 
#include <fstream.h>
#include <rw/db/db.h>
 
struct SOUND {
char *data;
size_t length;
};
 
void playSound(const SOUND& s) {
// Hypothetical function which plays the sound s.
}
 
istream& operator>>(istream& iStream, SOUND& sound) {
// Hypothetical function that reads the next sound from
// the stream into sound.
}
 
main() {
SOUND sound;
RWDBBlob aBlob;
RWDBDatabase myDatabase = RWDBManager::database("odb<ver>12d.dll",
"simba_driver", "myUserName", "myPassword", "");
 
// Play all the sounds stored in the table.
RWDBTable soundTable = myDatabase.table("sounds");
RWDBReader aReader = soundTable.reader();
while (aReader()) { //1
aReader >> aBlob; //2
sound.length = aBlob.length(); //3
sound.data = aBlob.data(); //4
playSound(sound);
}
// Add all the sounds in input file soundfile.dat to
// the database table sounds.
ifstream soundStream("soundfile.dat");
while (soundStream) { //5
soundStream >> sound;
if (soundStream.eof() || soundStream.fail() ||
soundStream.bad()) { //6
break;
}
RWDBBlob newBlob(sound.data, sound.length); //7
RWDBInserter anInserter = soundTable.inserter(); //8
anInserter << newBlob; //9
anInserter.execute(); //10
}
 
The example transfers data back and forth between SOUND, sound, and the RWDBBlobs, aBlob and newBlob.
The loop at //1 uses a reader to iterate through a table holding SOUNDs. The data is transferred to aBlob from the reader on //2. The RWDBBlob member functions length() and data() are used on //3 and //4 to set the related components of sound.
The loop at //5 reads from the input file stream and on //6, we ensure that we read data successfully and break out of the loop if we don't. On //7, we create an RWDBBlob instance newBlob using the data and length in the sound instance. The newBlob does not copy the data from sound, but simply holds a pointer to the data, saving memory. On //8 an RWDBInserter is produced from the soundTable. On //9 the newBlob is shifted into the inserter. On //10 the execute() method is invoked and the new sound is added to the database table.
Class RWDBBlob can be used effectively with the bulk interface. Please see Section 8.2.2, “Bulk Classes and RWDBBlob,” for an example.
7.4.6 RWDecimalPortable
Class RWDecimalPortable from the Essential Tools Module represents an arbitrary precision decimal fraction as a character string. This class exists mainly to provide a technique for retrieving exact precision numeric data types, such as monetary values. Some databases have methods for representing numeric values exactly. If the DB Interface Module fetched these values into float or double precision variables, there would be a risk of inaccuracy. Native C++ floating data types represent numbers using base 2; however, many noninteger decimal numbers, such as 0.1 and 19.7, cannot be exactly represented in base 2. For accounting or financial computations, the round-off error inherent in floating point arithmetic is unacceptable.
There are good reasons for using RWDecimalPortable:
*RWDecimalPortable provides exact representation and computation with decimal numbers. Using RWDecimalPortable objects is the same as using the built-in floating types, except that there is no round-off error when working with decimal fractions, since RWDecimalPortable uses base 10.
*Since RWDecimalPortable is part of the Essential Tools Module, it is the common representation used by Rogue Wave products.
The following example demonstrates the difference in accuracy between RWDecimalPortable and the built-in type double. In each case below, we add one cent to a zero-initialized variable one hundred times. We then remove one dollar, which should return the variable to zero. Using RWDecimalPortable, the test succeeds; the account balances. Using double, the value does not return to zero.
 
void usingDecimal() {
cout << "using RWDecimalPortable ... ";
RWDecimalPortable penny = "0.01";
RWDecimalPortable bank = 0;
for(int i = 100; i--;)
bank = bank + penny; // deposit 100 pennies
bank = bank - 1; // withdraw a dollar
cout << (bank == 0 ? "balances" : "doesn't balance") << endl;
}
 
void usingDouble() {
cout << "using double ... ";
double penny = 0.01;
double bank = 0;
for(int i = 100; i--;)
bank += penny; // deposit 100 pennies
bank -= 1; // withdraw a dollar
cout << (bank == 0 ? "balances" : "doesn't balance") << endl;
}
 
int
main() {
usingDecimal();
usingDouble();
return 0;
}
7.4.7 RWDBValue
In the DB Interface Module, class RWDBValue is the means of integrating data in the value layer with the pointer layer. RWDBValue automatically converts between compatible C++ and structured types, allowing the value layer to be type safe. An RWDBValue-based accessor and mutator are provided on RWDBTBuffer<T>, giving the Open SQL interface the flexibility needed to support the value layer. RWDBValue also adds null/not null semantics to the primitive types.
In application programs, RWDBValue is especially useful when working with results where the type of data is not known. Programs can read data directly into RWDBValue instances, and then interrogate the instance to find out what kind of data was placed in it. RWDBValue instances can be held in collections and passed without knowing what type of data they are holding. Conversion methods on RWDBValue allow applications to handle unknown data in a robust, predictable manner.
You will find a complete entry for RWDBValue in the SourcePro C++ API Reference Guide, but its main components are listed here:
*An enumerated type, ValueType, which codes the datatype of the stored value. The possible ValueTypes are NoType, Tiny, UnsignedTiny, Char, UnsignedChar, Int, UnsignedInt, Short, UnsignedShort, Long, UnsignedLong, LongLong, UnsignedLongLong, Float, Double, LongDouble, String, Blob, Date, DateTime, Duration, Decimal, UString, WString, and MBString. To avoid polluting the global name space, all enums are scoped within classes. To refer to a member of the enumeration outside the scope of RWDBValue, use the scoping operator ::, as in RWDBValue::String, for example.
*A friend, rwdbNull, which is of type RWDBValueManip, and represents a literal NULL value. There is a constructor and an assignment operator that allow RWDBValue instances to be formed from rwdbNulls and other RWDBValueManips.
*A C++ union that can store a Long, Unsigned Long, Long Long, Unsigned Long Long, Double, Long Double, RWCString, RWDBMBString, RWWString, RWBasicUString, RWDecimalPortable, RWDBDateTime (deprecated), RWDBDuration, or RWDBBlob.
*Member functions type()and isNull()to access the value's datatype and NULL attribute, respectively. Note that isNull() cannot be used to determine if a value extracted from an RWDBReader is NULL. Please refer to Section 4.4.2, “Reading Tables: Class RWDBReader,” and the entries in the SourcePro C++ API Reference Guide for RWDBNullIndicator and RWDBReader for more information.
*Constructors and assignment operators that allow an RWDBValue to be constructed or copied from any C++ primitive or from any structured type of the DB Interface Module.
*The member function canConvert(ValueType), which detects whether or not a given value can be converted to a specified type.
*A collection of conversion routines such as asInt(), asDouble(), asString(), and so on, which convert a given value to the type implied by the routine name.
*The member function typeString() returns the datatype of self in the form of a string.
*Member functions isEqual(), compareTo(), binaryStoreSize(), saveGuts(), and restoreGuts(), which are required to make RWDBValue persistable, as defined in the Essential Tools Module. RWDBValue is derived from RWCollectable.
As mentioned earlier, RWDBValue is used internally to integrate the value and pointer layers of the DB Interface Module. Whenever data arrives from a database, routines in the database layer interrogate the database API for its type, and store the data into RWDBTBuffer<T>, where T is the primitive or structured type of the DB Interface Module best able to hold the data. RWDBTBuffer<T> provides an accessor that automatically converts data elements into RWDBValue instances for the value layer. Whenever the value layer requests data from the pointer layer, RWDBValue’s canConvert() routine is consulted to determine whether or not the conversion can be made. If not, an error condition arises. Otherwise, the appropriate as<Type>()routine is invoked to convert to the requested type.
7.4.8 Internationalization Data Types
Data types RWDBMBString, RWWString, and RWBasicUString are used for supporting internationalization in SourcePro DB. They are explained in detail in Chapter 13, “Internationalization.”