Product Documentation

FairCom ISAM for C

Previous Topic

Next Topic

Simple File Maintenance

lowlevel.c, the first sample program, introduces FairCom DB low-level functions.

Throughout this series of sample programs, we use the following data record structure for our data file:

struct INVENTORY { /* data record format */

COUNT delete_flag; /* delete flag byte */

TEXT item[26]; /* Item ID - the key value */

TEXT descrip[51]; /* Item Description */

TEXT location[10]; /* Item Location */

double quantity; /* Quantity on hand */

double cost; /* Unit Cost */

TEXT padding[23]; /* unused padding */

} invent;

We will build just one index, using the item field.

We have allocated one field at the beginning of the record structure for the delete flag. It is best to set aside space for this in your structure. If you decide that you want to use a status value for each record, you could use this same variable for the status. Just remember to not use the value of 0xff as a status value, as that represents a deleted record to FairCom DB.

The program mainline is the same in all of the examples of this series:

main (argc,argv)

int argc;

pTEXT argv[];

{

void create(),initial(),database(),closeit();


if (argc == 1)

create(); /* create the database files */

else

initial(); /* initialize the system */


database(); /* perform database routines */

closeit(); /* close the files, and any other ending stuff*/

}

If there is any value passed on the command line, we will assume that you want to create the database. Otherwise, we assume that the database has already been created. You could have a more sophisticated startup than this, where you try to open the files first, and if that fails then trying to create them.

After the file system has been set up, we call database() to perform the operations, and when done we call closeit() to finish up.

In This Section

Initialize the system

Error Handling

Adding Data

Delete a key

Close the system

Previous Topic

Next Topic

Initialize the system

InitCTree

Before we can do any FairCom DB actions we must initialize the FairCom DB buffers with the InitCTree() function call. This allocates the memory for index buffers, (10 in our example), data and index file structures, (2 in our example), and establish the number of sectors available in each FairCom DB node, (4 in our example).

In single user systems, improve performance by increasing the number of index buffers, the first InitCTree() parameter. We typically suggest a maximum of 6 buffers per active index. The only reason that you might want to use a smaller number is if you cannot afford to allocate the space. In Standalone Multi-user systems the number of buffers should be set to 6 no matter how many indexes you are using, as local buffering will not be used.

For most situations, you should leave the number of sectors per node at 4, which allocates 512 bytes per node. It is important to be consistent with this parameter, as an index created with one value cannot be used if you use a smaller value in a subsequent call to InitCTree().

For additional information on index buffer and sector settings see Performance Optimization.

Create and Open Files

Once we have initialized FairCom DB, we can go on to either create or open the data files and indexes. When using low-level functions you must explicitly create/open each file separately. The create functions are almost identical to the open functions, except that the create functions have additional parameters that are stored in the header record of the data file or index. This is information such as the record or key length, whether the data file is to have fixed- or variable-length records, and so forth.

Two files are created in our example. The first file is the data file, with a fixed record length of 128 bytes. The xtdsiz parameter is set to 4096, which means that every time the file is extended in size it will be extended by 4096 bytes. This improves the efficiency of the system. Note that we have included a file mode of ctSHARED, to allow multiple users access to the data file. The second file is the index, with a key length of 25. We are not allowing duplicate keys.

Previous Topic

Next Topic

Error Handling

If an error occurs in one of the FairCom DB functions, we call one of two error handling functions. terminate() is called if the error occurs at a time where we don’t want to continue until the error is corrected. func_error() is called when the situation is not bad enough for the program to be terminated.

uerr_cod

isam.c has a very basic error handler in func_error(). Based on the global error variable uerr_cod, we print a number of different error messages. If the error is not one that we have a specific message for, we just print the error number. You certainly can create a more sophisticated error handling.

terminate() prints an error message that we pass to it, and calls func_error(). The next function invoked is StopUser(). In single user and Standalone Multi-user situations, this function is ignored. When using the FairCom Server the application must call this function before exiting the program for any reason. It tells the FairCom Server that this application is finished with FairCom DB services, freeing server resources to be used by other applications.

Previous Topic

Next Topic

Adding Data

Now that the files are open, we can add records. The function datadd() is used.

The first step is to initialize the data record buffer. The only thing required for FairCom DB is setting the delete flag to a value other than 0xff. It is risky to not initialize this variable with some safe value.

The function getfld() captures data from the screen. As supplied, there is no form of error checking. We leave this part of the program up to you.

translate key

After we have the data that we want to enter, we call transkey() to format the key the way we want it. This is a function created for this sample program. In this case, we are doing two things. First, we translate all of the alpha characters to upper case. For the purpose of our example, we have decided that we don’t want to distinguish between upper case and lower case characters in our keys. This keeps the collating sequence for the keys in a usable form. The second step of the key translation is most important. We pad the key to its full length with null characters.

Padding the key is an extremely important step. FairCom DB keys are NOT treated as standard null-terminated C strings. The full length of the key, as specified when the index was created, is important even if the actual value used is shorter. If you are using string values as keys you must pad them to their full length, or you may not be able to retrieve them later.

For example, let’s look at a situation where we have a key length of 10, and we add two key values. The first key value is
“ABCDEFGHIJ”, which is 10 characters long. After adding this key to the index we add another, “XYZ”, which is only 3 characters long. While adding the keys we use the same buffer variable to store the values. If we look at what is stored in the variable when “XYZ” is there, we see that the contents of the variable are “XYZýEFGHIJ”, where ý represents the null character that terminates the string. All 10 characters are added to the index as the key! Later, when we retrieve this record, we have a hard time finding an exact match to the key when we enter the value “XYZ”. FairCom DB looks for the rest of the information. By padding the values to their full length with some consistent character, such as a null or space, we will find that our programs work as expected.

GetKey

Now that we have a key to work with, we want to see if the key already exists in the index. We use GetKey() to look for an exact match. If the function returns a value, we have found a match, and we don’t want to try to add the key again. A zero means no match was found, and we check uerr_cod to see if there was an error or not.

NewData

The process of adding the key to the index and writing the record to the data file takes several steps. First, call NewData() to obtain a data record position to write the record. FairCom DB keeps a list of deleted records and reuses those records when you call NewData(). If there are no deleted records, NewData() automatically extends the file to gain a new record position.

WriteData

Once we have a record position, we write the data record to the file with WriteData(). If an error occurs, we return the data record position to the file with ReleaseData().

AddKey

With the record is in the data file, we add the key to the index with AddKey(). We do this after writing the data record for two reasons. First, AddKey() requires the data record position to store with the key value. Second, since most access to the file will be through the index, we want to be sure that the record exists before the key is written. In a multi-user environment we don’t want the key written and accessed by another user before the data record is written.

LockCtData

Any time after the call to NewData() that we leave this function, due to a failure of a function or the successful completion of the process, we call LockCtData(). When NewData() acquires a data record, it puts a lock on the record that must be released before other users can access it. On a single user system, LockCtData() is ignored.

Previous Topic

Next Topic

Delete a key

The datdel() function deletes a key from the index and deletes the record from the data file. Once again we use the transkey() function to translate our input key to the format that we want to use. GetKey() is used to locate the key in the index.

ReadData

If the key is found, we use ReadData() to read the record so that we can display it. You might decide that you want to ask the user if this is the correct record to display.

DeleteKey and ReleaseData

DeleteKey() deletes the key from the index. ReleaseData() returns the data record to the list of deleted records.

Previous Topic

Next Topic

Close the system

When the application finishes, it must shut down the FairCom DB system and close files properly. Each open data file and index must be closed with CloseCtFile(). If a file has been created and/or modified during this run of the application, and it is NOT closed properly, the file will be considered damaged. Information stored in the header record about the status of the file is not always written to disk until the file is closed. If the file is not closed, and the information in the header is incorrect, the next time the file is opened you will receive an error indicating the file has been corrupted. Either throw the file away or rebuild it. See ctixmg.c for an example of how to use the incremental ISAM function RebuildIFile() to rebuild a file.

As noted in the discussion of terminate(), the application must call StopUser() before exiting. This tells the FairCom Server the application is finished, releasing resources for other users. When not using the FairCom Server, StopUser() is ignored.

TOCIndex