OOFILE | Downloads | Purchasing | Press | Services | Company Information | Soapbox | References | F.A.Q. | HOME |
This sample tests the database backend by creating a single table and storing and retrieving indexed data.
The include files are:
#include "oofile.h" // the general oofile library
#include "oofios.h" // an easy way to get your stream libraries, regardless of platform
#include "ooftst01.h" // the declarations for the database classes we will be using in this test.
int main()
{
cout << "OOFILE Validation Suite - Test 1\n"
<< "Simple test to store some data and retrieve it\n"
<< "in a single-table database, with no relations\n"
<< endl;
Declaration of the database we will be using: Basic information
Testing: declaration
of database class
Testing: declaration of table
class
TEST_CONNECT theDB; dbPeople People;
We can use separate files, or a single container file and this sample uses separate files
theDB.useSeparateFiles();
The following is a bit of complicated filename logic to support different backends with this one test program. This code determines what platform we are running under and assigns a name for the database file accordingly.
#ifdef TESTING_CTREE #ifdef _Macintosh // path handling only on the Mac at present const char* kExistsName = ":ooftst01:People.dat"; const char* kDatabaseName = ":ooftst01:"; #else const char* kExistsName = "People.dat" const char* kDatabaseName = ""; #endif
#else #ifdef TESTING_DBASE #ifdef _Macintosh const char* kExistsName = ":ooftst01:People.dbf"; const char* kDatabaseName = ":ooftst01:"; #else const char* kExistsName = "People.dbf" const char* kDatabaseName = ""; #endif
#else // Persistent RAM, no option of single file #ifdef _Macintosh const char* kDatabaseName = ":ooftst01:ooftst01.db"; #else const char* kDatabaseName = "ooftst01.db"; #endif const char* kExistsName = kDatabaseName; #endif #endif
This segment shows us how to open or create the database whose name was given in the code above.
If the file already exists we use openConnection() to open the file and
use deleteAll() to delete everything so that it is empty. More
tests on deleting.
If it does not exist, we create the file using newConnection().
Testing: function
dbConnect::fileExists()
Testing: function
openConnection()
Testing: function deleteAll()
Testing: function newConnection()
if (dbConnect::fileExists(kExistsName)) {
theDB.openConnection(kDatabaseName);
cout << "Just opened...\n" << theDB << endl << endl;
People.deleteAll();
}
else {
theDB.newConnection(kDatabaseName);
}
We then add records to the database by calling the method AddTestData() defined in ooftst01.h
People.AddTestData();
This line gives you the option of sorting before we print the first time. It is commented out at the moment as it isn't necessary.
// People.setSortOrder(People.LastName);
This line shows us how to print the database to
standard output.
Note that in this case, we pass the whole database to cout, so all the fields
will be printed. From now on we will use dbViews to restrict the fields listed.
cout << "Listing records\n" << People;
Views allow us to make a sub-set of the table so that we don't have to print all fields but we can still access the whole table. They can be passed to cout or fed to graphs, reports or listing tables.
This is how to declare a view. Note that we give it a name and pass it the table that it will be a view of.
Testing: declaration of dbView
dbView justNames(People);
This is how to put our chosen fields into a view. We pass them in the same
way as we pass things to streams.
Now, only these fields will print when we pass this view of the database to cout.
Testing: adding fields to a dbView
justNames << People.LastName << People.OtherNames;
Basic information Need to know more?
To sort the database, we must choose a field we would like it to be sorted
by. This code tests sorting by a character field (a string). To sort, we pass
the field we want to setSortOrder().
Note: we only pass the view to cout, so only the last and first names of the
selected records will be printed.
Testing:
setting sort order to character field
Testing: function setSortOrder()
Testing: passing dbView to cout
People.setSortOrder(People.OtherNames); cout << "Listing records in OtherNames order\n" << justNames << endl;
Basic information Need to know more?
To search, we must specify a field to be searched and the conditions it must
fulfil.
Here, we are retrieving all records that have the word "Taylor" in the
field "LastName".
This particular search uses the index on this field rather than calling the
method search(). This is known as searching by query and is identical
to the use of the function search(). Note, also, that we are doing the search
right in the cout stream.
Testing: single field query search on indexed character field.
cout << "Now retrieving by index\n"; cout << "Retrieving Taylor: " << People[People.LastName=="Taylor"].LastName << endl;
This time, we search a numeric field. We also are not putting the search directly into cout, but show how you can just declare the search and it wil still hold when you pass the table to cout. Here, however, we are passing only a single filed to cout, instead of the whole database.
Testing:
single field query search on indexed numeric field
Testing: passing single
field to cout
cout << "Retrieving Smith by Salary: "; People[People.Salary==0]; cout << People.LastName << endl << endl;
Now we try using the function search(). Note that we attach this to the end of the table we wish to search and then pass it the query we wish to search. Note we are again using the dbView "justNames" that we defined earlier.
Testing: function search()
Testing:
single field search() on indexed character field
People.search(People.LastName=="Dent"); cout << "Listing two Dent records: " << endl << justNames << endl;
Cloning consists of making a full copy of a table (eg from some search results) that is independant of the original and can be manipulated seperately.
We will now produce a subset of the search results obtained earlier.
We start by making a copy of the table (naming it entrepeneurs). Ths is simply a
matter of defining a new one and initialising it with the original.
cout << endl << "now producing a subset of Taylor & Dent" << endl; dbPeople entrepeneurs = People;
Now we will do various manipulations of this table.
First, we will set a search for entrepeneurs.
Testing: single field search() on cloned table
entrepeneurs.search(entrepeneurs.LastName=="Taylor");
Now, we add all the records from the People table by using the += operator on the two tables. We are adding the results of the search on people and the search on entrpeneurs and thus we combine the results of two individual searches on the original table. Note here that the dbView is created temporarily in the cout expression. This is useful as you are essenitially passing only one item to cout (the dbView).
Testing: +=operator on table
Testing: creating temporary
dbView
Testing: passing
temporary dbView to cout
entrepeneurs += People;
cout << (dbView(entrepeneurs) << entrepeneurs.LastName
<< entrepeneurs.OtherNames) << endl;
Here we clone again and print the number of records in it to cout to test if it cloned correctly. Note that we use the function count(), this takes a selection and returns the number of records currently in it.
Testing: cloning of a clone
Testing: function count()
dbPeople savedEnts = entrepeneurs; cout << "Just cloned a selection of " << savedEnts.count()<< " records" << endl;
This section tests the operator &= which uses set intersection to reduce the table entrepeneurs to the records that contain the LastName Dent (this is the current selection of the People database).
cout << endl << "now reducing original via Intersection to Dent" << endl; entrepeneurs &= People; cout << entrepeneurs;
This section tests the unary operator ~. This should invert the selection so that the only records remaining in the table are those that don't contain the LastName of "Dent". We again create a temporary dbView in the cout stream.
cout << endl << "now Inverting to Smith & Taylor" << endl; ~entrepeneurs; cout << (dbView(entrepeneurs) << entrepeneurs.LastName << entrepeneurs.OtherNames) << endl;
Here we test if the second cloned selection is still the full table that entrepeneurs was when we first cloned it.
cout << "Show the cloned selection is still Dent & Taylor" << endl << (dbView(savedEnts) << savedEnts.LastName << savedEnts.OtherNames) << endl;
This section tests the -= operator. This takes out all the records that belong to the table we are subtracting. As the entrepeneurs table only contains records that don't have the LastName of "Dent", we will be left only with Dent records.
cout << endl << "now reducing via Difference to Dent" << endl; savedEnts -= entrepeneurs; cout << (dbView(savedEnts) << savedEnts.LastName << savedEnts.OtherNames) << endl;
Here, we demonstrate going to a specific relative record in a selection then going to the previous record This would be common in a GUI by double-clicking a line in a browser, then pressing Prev Record.
First, we undo the search selection we created previously, by using selectAll(). This selects every record in the database (as though we hadn't made selections previously -> note that the term search selection and the term selection are interchangable. We will then sort the database (this time using the character field LastName).
People.selectAll(); People.setSortOrder(People.LastName);
gotoRecord(n) takes us to the nth record in the sorted list.
Testing: relative record retrieval using gotoRecord(n)
cout << "Retrieving Taylor by relative record number: "; People.gotoRecord(3);
We can then easily find the previous record on the list using prev(). This finds the record just prior to the current one -> referring to the positions of records relative to the currently set sort order (ie, this could retrieve a different record if the sort order was different).
Testing:
retrieval of previous record (relative to current sort order) using prev()
Testing: function prev()
cout << People.LastName << endl << endl; cout << "Retrieving previous record Smith: "; People.prev(); cout << People.LastName << endl << endl;
This section does a more complicated search,
using a date field and accessing the current date to compare it with.
We test whether the record's LastPaid field is greater than a fortnight before
the current date. This shows how to use dbDate::currentDate(), and also how to
subtract a set number of days from a date. More
tests on dbDates.
Testing:
single field search() on indexed date field
Testing:
comparison search() -> (x > y)
Testing:
access of current date using dbDate::currentDate()
Testing: subtraction
of constant from dbDate
cout << "Now finding people who've been paid in the last 2 weeks " << endl;
People.search(People.LastPaid > dbDate::currentDate()-14);
cout << (dbView(People) << People.LastName << People.OtherNames
<< People.LastPaid) << endl;
This section demonstrates the value() call, which is mainly for use in the LHS of if() statements instead of cast operators. It finds people with the same salary as the first entrepeneur. This is a silly way to do it - a search would be faster. This is just an example to show the use of this command
NOTE: the value() on the RHS of the if() is redundant as the 'operator long()' would be invoked automatically by the context. It is used here because it looks nice and symmetrical and so helps readability.
The use of start() takes us to the first record in the table.
We then reset the selection for People by using selectAll() then iterate through
the table and test the value of salary for each record to see if they match. The
function more() tells us if there are any more records in the list after the
current one. The function next() gets the record after the current one (as prev()
got the one before the current).
Testing: function start()
Testing: iteration through table
Testing: function more()
Testing: function next()
Testing: field evaluation
using value()
Testing: ==operator on fields
entrepeneurs.start();
People.selectAll();
for (People.start(); People.more(); People.next()) {
if (People.Salary.value() == entrepeneurs.Salary.value()) //******
cout << "Matching Salaries of : "
<< People.Salary << " for "
<< People.LastName << ", "
<< entrepeneurs.LastName
<< endl << endl;
}
cout << "Test Completed" << endl;
return EXIT_SUCCESS; }
(c) Copyright A.D. Software 1994-2000 (All Rights Reserved).
Last Updated: 9th September 2001