// ------------------------------- //
// -------- Start of File -------- //
// ------------------------------- //
// ----------------------------------------------------------- //
// C++ Source Code File Name: testprog.cpp
// Compiler Used: MSVC, BCC32, GCC, HPUX aCC, SOLARIS CC
// Produced By: DataReel Software Development Team
// File Creation Date: 09/18/1997
// Date Last Modified: 06/17/2016
// Copyright (c) 2001-2024 DataReel Software Development
// ----------------------------------------------------------- //
// ------------- Program Description and Details ------------- //
// ----------------------------------------------------------- //
/*
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
Test program for the InfoHog database API.
*/
// ----------------------------------------------------------- //
#include "gxdlcode.h"
#if defined (__USE_ANSI_CPP__) // Use the ANSI Standard C++ library
#include <iostream>
using namespace std; // Use unqualified names for Standard C++ library
#else // Use the old iostream library by default
#include <iostream.h>
#endif // __USE_ANSI_CPP__
#include "fstring.h"
#include "infohog.h"
#include "gxfloat.h"
#include "ustring.h"
#ifdef __MSVC_DEBUG__
#include "leaktest.h"
#endif
// Type definitions
typedef FString Name_t; // String type
typedef gxFLOAT64 Price_t; // Floating point type
typedef gxINT32 StockNumber_t; // Integer type
void PausePrg()
// Function used to pause the program.
{
cout << "\n";
cout << "Press enter to continue..." << "\n";
cin.get();
}
void ClearInputStream(istream &s)
// Function used to clear the input stream.
{
char c;
s.clear();
while(s.get(c) && c != '\n') { ; }
}
int Quit()
{
cout << "Exiting..." << "\n";
return 0;
}
void InputData(Name_t &key)
// Function used to read a string from the input stream.
{
cout << "Item name: ";
cin >> key;
}
void DisplayItem(InfoHog<Name_t> &infohog, int full = 1)
// Function used to print a infohog list object to the stdout.
{
cout << "\n";
if(infohog.GetMember(0)) {
cout << "Item Name = " << (char *)infohog.GetMember(0) << "\n";
}
if(full) {
if(infohog.GetMember(1)) {
cout << "Stock Number = "
<< *((StockNumber_t *)infohog.GetMember(1)) << "\n";
}
if(infohog.GetMember(2)) {
cout.setf(ios::showpoint | ios::fixed);
cout.precision(2);
cout << "Price = $"
<< *((Price_t *)infohog.GetMember(2)) << "\n";
}
}
}
void PrintBtreeHeader(POD *DB, unsigned index_number)
{
gxBtreeHeader hdr;
UString intbuf;
DB->Index(index_number)->ReadBtreeHeader(hdr);
cout << "B-tree header information" << "\n";
cout << "Node order: " << hdr.node_order << "\n";
cout << "Key size: " << hdr.key_size << "\n";
cout << "Number of nodes: " << hdr.n_nodes << "\n";
cout << "B-tree height: " << hdr.btree_height << "\n";
intbuf << clear << (FAU_t)hdr.root;
cout << "Root Address: " << intbuf.c_str() << "\n";
cout << "Number of trees: " << hdr.num_trees << "\n";
cout << "\n";
}
void Menu()
// Console based user menu.
{
cout << "(A, a) Add object to the database" << "\n";
cout << "(B, b) Build a test database" << "\n";
cout << "(C) Change data members" << "\n";
cout << "(c) Change object" << "\n";
cout << "(D, d) Delete object from the database" << "\n";
cout << "(F, f) Find item by name" << "\n";
cout << "(L, l) List without using index file" << "\n";
cout << "(H, h, ?) Help (prints this menu)" << "\n";
cout << "(I, i) List using the index file" << "\n";
cout << "(Q, q) Quit" << "\n";
cout << "(S, s) Display B-tree stats" << "\n";
cout << "(X, x) Compare the index file to the data file" << "\n";
cout << "(Y, y) Rebuild the index file" << "\n";
}
void BuildDatabase(POD *DB)
// Function used to build a test database.
{
// Ensure that the in memory buffers and the file data
// stay in sync during multiple file access.
DB->TestDatabase();
const int NUM_OBJECTS = 1000;
int i = 0;
InfoHog<Name_t> infohog(DB);
char name[InfoHogNameLength];
StockNumber_t stock_number = (StockNumber_t)2000;
Price_t price = (Price_t)0.95;
cout << "Adding " << NUM_OBJECTS << " objects to the database..." << "\n";
for(i = 0; i < NUM_OBJECTS; i++) {
sprintf(name, "Item-%i", i);
stock_number += i;
price += i;
Name_t key(name);
if(!infohog.SetMember(&key, sizeof(key), 0)) {
cout << "Could not set data member 0" << "\n";
return;
}
if(!infohog.SetMember(&stock_number, sizeof(stock_number), 1)) {
cout << "Could not set data member 1" << "\n";
return;
}
if(!infohog.SetMember(&price, sizeof(price), 2)) {
cout << "Could not set data member 2" << "\n";
return;
}
if(!infohog.WriteObject()) {
cout << "Could not add object number " << i << " to the database"
<< "\n";
return;
}
}
// Flush the database files after a batch insert
DB->Flush();
}
void ListInOrder(POD *DB, unsigned index_number)
// List the contents of the database using the index file.
{
// Ensure that the in memory buffers and the file data
// stay in sync during multiple file access.
DB->TestDatabase();
InfoHog<Name_t> infohog(DB);
InfoHogKey<Name_t>key, compare_key;
gxBtree *btx = DB->Index(index_number);
// Walk through the tree starting at the first key
if(btx->FindFirst(key)) {
if(!infohog.ReadObject(key.ObjectID())) {
cout << "Error reading the object" << "\n";
return;
}
DisplayItem(infohog);
while(btx->FindNext(key, compare_key)) {
if(!infohog.ReadObject(key.ObjectID())) {
cout << "Error reading the object" << "\n";
return;
}
DisplayItem(infohog);
}
}
}
void ListInOrder(POD *DB)
// List contents of the database without using the index file.
{
// Ensure that the in memory buffers and the file data
// stay in sync during multiple file access.
DB->TestDatabase();
FAU_t addr = (FAU_t)0;
InfoHog<Name_t> infohog(DB);
unsigned count = 0;
ClearInputStream(cin); // Clear input stream
while(1) {
addr = DB->OpenDataFile()->FindFirstObject(addr);
if(!addr) break;
if(!infohog.ReadObject(addr)) {
cout << "Error reading the object" << "\n";
return;
}
DisplayItem(infohog);
count++;
if(count == 2) {
PausePrg();
count = 0;
}
}
}
void AddItem(POD *DB)
// Function used to add an object to the database.
{
// Ensure that the in memory buffers and the file data
// stay in sync during multiple file access.
DB->TestDatabase();
Name_t key;
InputData(key);
InfoHog<Name_t> infohog(DB);
infohog.SetMember(&key, sizeof(key), 0);
int exists = infohog.FindObject();
if(exists) {
cout << "Item: " << key << " already exists" << "\n";
return;
}
int stock_number;
double price;
cout << "Enter the items's stock number: ";
cin >> stock_number;
if(cin) {
StockNumber_t sn = stock_number;
infohog.SetMember(&sn, sizeof(StockNumber_t), 1);
cout << "Enter the items's price: $";
cin >> price;
}
else {
cout << "Invalid entry. Object not added!" << "\n";
return;
}
if(cin) {
Price_t p = price;
infohog.SetMember(&p, sizeof(Price_t), 2);
}
else {
cout << "Invalid entry. Object not added!" << "\n";
return;
}
ClearInputStream(cin);
if(!infohog.WriteObject()) {
cout << "Could not add object to the database" << "\n";
}
DB->Flush(); // Flushing the database buffers following manual insertion
}
void DeleteItem(POD *DB)
// Function used to delete an object from the database.
{
// Ensure that the in memory buffers and the file data
// stay in sync during multiple file access.
DB->TestDatabase();
Name_t key;
InputData(key);
InfoHog<Name_t> infohog(DB);
infohog.SetMember(&key, sizeof(key), 0);
if(!infohog.DeleteObject()) {
cout << "Could not find item: " << key << " in the database" << "\n";
return;
}
cout << "Deleted item: " << key << "\n";
DB->Flush(); // Flushing the database buffers following manual deletion
}
void FindItem(POD *DB)
// Function used to find an object in the database.
{
// Ensure that the in memory buffers and the file data
// stay in sync during multiple file access.
DB->TestDatabase();
Name_t key;
InputData(key);
InfoHog<Name_t> infohog(DB);
infohog.SetMember(&key, sizeof(key), 0);
if(!infohog.FindObject()) {
cout << "Could not find item: " << key << " in the database"
<< "\n";
return;
}
cout << "Found item: " << key << "\n";
if(!infohog.ReadObject()) {
cout << "Error reading the object" << "\n";
return;
}
DisplayItem(infohog);
}
void Compare(POD *DB)
// Function used to compare the contents of the data file to the
// index file.
{
if(!DB->UsingIndex()) {
cout << "This database is not using the indexing sub-system" << "\n";
return;
}
// Ensure that the in memory buffers and the file data
// stay in sync during multiple file access.
DB->TestDatabase();
InfoHog<Name_t> infohog(DB);
cout << "\n";
cout << "Comparing the index file to the data file..." << "\n";
int rv = infohog.CompareIndex(0);
if(!rv) {
cout << "The index file does not match the data file!" << "\n";
cout << "The index file needs to be rebuilt." << "\n";
cout << "\n";
return;
}
cout << "The index file checks good" << "\n";
cout << "\n";
}
void Rebuild(POD *DB, const char *fname)
// Function used to rebuild the index file if the index file
// entries no longer match the data file entries.
{
if(!DB->UsingIndex()) return;
// Ensure that the in memory buffers and the file data
// stay in sync during multiple file access.
DB->TestDatabase();
InfoHog<Name_t> infohog(DB);
cout << "\n";
cout << "Rebuilding the index file..." << "\n";
// Rebuild index number 0
int rv = infohog.RebuildIndexFile(fname, 0,
InfoHogNumTrees, InfoHogNodeOrder);
if(!rv) {
cout << "The index file was not rebuilt!" << "\n";
cout << "\n";
return;
}
cout << "The index file was rebuilt." << "\n";
cout << "A new index file named " << fname << " was created.";
cout << "\n";
}
void ChangeItem(POD *DB, int change_object = 1)
{
Name_t key;
InputData(key);
InfoHog<Name_t> infohog(DB);
infohog.SetMember(&key, sizeof(key), 0);
if(!infohog.FindObject()) {
cout << "Could not find item: " << key << " in the database"
<< "\n";
return;
}
cout << "Found item: " << key << "\n";
if(!infohog.ReadObject()) {
cout << "Error reading the object" << "\n";
return;
}
DisplayItem(infohog);
ClearInputStream(cin);
cout << "Entering changes to this object..." << "\n";
Name_t new_key;
InputData(new_key);
InfoHog<Name_t> infohog_new(DB);
infohog_new.SetMember(&new_key, sizeof(new_key), 0);
int stock_number;
double price;
cout << "Enter the items's stock number: ";
cin >> stock_number;
if(cin) {
StockNumber_t sn = stock_number;
infohog_new.SetMember(&sn, sizeof(StockNumber_t), 1);
cout << "Enter the items's price: $";
cin >> price;
}
else {
cout << "Invalid entry. Object not added!" << "\n";
return;
}
if(cin) {
Price_t p = price;
infohog_new.SetMember(&p, sizeof(Price_t), 2);
}
else {
cout << "Invalid entry. Object not added!" << "\n";
return;
}
ClearInputStream(cin);
int find = 0;
if(change_object) { // Change the object
if(!infohog.ChangeObject(infohog_new, find)) {
cout << "Could not change this object" << "\n";
return;
}
return;
}
// Change the object member by member
if(!infohog.ChangeMember(infohog_new.GetMember(0),
infohog_new.GetMemberLen(0), 0, find)) {
cout << "Could not change key member" << "\n";
return;
}
if(!infohog.ChangeMember(infohog_new.GetMember(1),
infohog_new.GetMemberLen(1), 1, find)) {
cout << "Could not change member 1" << "\n";
return;
}
if(!infohog.ChangeMember(infohog_new.GetMember(2),
infohog_new.GetMemberLen(2), 2, find)) {
cout << "Could not change member 2" << "\n";
return;
}
}
int main(int argv, char **argc)
{
#ifdef __MSVC_DEBUG__
InitLeakTest();
#endif
char rev_letter = gxDatabaseRevisionLetter; // Set the default rev letter
if(argv == 2) { // Set a specified revision letter
rev_letter = *argc[1];
if(rev_letter == '0') rev_letter = '\0';
// Valid rev letters are:
// Rev 0
// Rev 'A' or 'a'
// Rev 'B' or 'b'
// Rev 'C' or 'c'
// Rev 'D' or 'd'
// Rev 'E' or 'e'
// NOTE: The gxDatabase class will set invalid revision letters
// to the version set by the gxDatabaseRevisionLetter constant.
}
InfoHogKey<Name_t> key_type;
FStringCaseCompare = 0; // Do not compare the case of FString objects
const char *data_file = "grocery.ihd";
const char *index_file = "grocery.ihx";
// Create or open an existing database using a single index file
gxDatabaseAccessMode mode = gxDBASE_READWRITE;
BtreeNodeOrder_t order = InfoHogNodeOrder;
int num_trees = InfoHogNumTrees;
int use_index = InfoHogUseIndexFile;
FAU_t static_size = InfoHogStaticArea;
__SBYTE__ df_rev_letter = rev_letter;
__SBYTE__ if_rev_letter = rev_letter;
POD pod;
gxDatabaseError err = pod.Open(data_file, index_file, key_type,
order, mode, use_index,
static_size, num_trees, df_rev_letter,
if_rev_letter);
// Test for any errors
if(err != gxDBASE_NO_ERROR) {
cout << gxDatabaseExceptionMessage(err) << "\n";
return 1;
}
char c;
int rv = 1;
Menu();
while(rv) {
if (!cin) {
ClearInputStream(cin);
if (!cin) {
cout << "Input stream error" << "\n";
return 0;
}
}
cout << '>';
cin >> c;
if (!cin) continue;
switch(c) {
case 'a' : case 'A' :
ClearInputStream(cin); AddItem(&pod);
break;
case 'b' : case 'B' :
BuildDatabase(&pod);
break;
case 'c' :
ClearInputStream(cin); ChangeItem(&pod);
break;
case 'C' :
ClearInputStream(cin); ChangeItem(&pod, 0);
break;
case 'd' : case 'D' :
ClearInputStream(cin); DeleteItem(&pod);
break;
case 'f' : case 'F' :
ClearInputStream(cin); FindItem(&pod);
break;
case 'l' : case 'L' :
ListInOrder(&pod);
break;
case 'i' : case 'I' :
ListInOrder(&pod, 0);
break;
case 'h' : case 'H' : case '?' : Menu(); break;
case 'q' : case 'Q' : rv = Quit(); break;
case 's' : case 'S' : PrintBtreeHeader(&pod, 0); break;
case 'x' : case 'X' :
Compare(&pod); break;
case 'y' : case 'Y' :
Rebuild(&pod, "newindex.ihx"); break;
default:
cout << "Unrecognized command" << "\n";
}
}
return 0;
}
// ----------------------------------------------------------- //
// ------------------------------- //
// --------- End of File --------- //
// ------------------------------- //