C-Index/II Home Page
|CI2 Home|Literature|Support|Documentation|Y2K|Order|License|


Chapter 3 Using the Single-Key Functions

This chapter provides a general discussion of using the Single-Key functions of C-Index/II, and should be read before using these functions. A full description of all aspects of each function will be found in the C-Index/II Reference Guide.

Description of Single-Key Functions

The C-Index/II single-key functions are designed to be a generalized index and data storage system that does not make any assumptions about the nature of the information that is being stored. This allows considerable flexibility in the usage of the system.

One method of using C-Index/II is as a standard B+Tree index file manager for the storage of keys pointing to data records. The keys and corresponding data record numbers are stored in the index file and these values are used to locate the data in a separate, fixed-length record file.

C-Index/II however, also offers many other advantages. For example, another filing method unique to C-Index/II is to have the data associated with the key actually stored with the key. In this case, the data record number is not needed other than to resolve duplicate keys. This feature opens up many new programming possibilities not available from a standard B+Tree system. In addition, the data record stored with the key may vary in length up to 30K bytes. This feature allows the system to be used as a generalized data storage system in which information is retrieved by key value. Often an application will need to keep track of many small pieces of information. C-Index/II allows the programmer to store these pieces in an index file by keyed value without concern for the exact location of the pieces in the file. This use of C-Index/II can eliminate the need for slow and cumbersome searches through standard C files, and in this situation, C-Index/II is easier to use than standard C language file access routines.

 

C-Index/II Filing Structure

The C-Index/II system allow multiple files to be open at once. For example:

 

File 1 File 2 File 3 .... File n

 

The maximum number of files open is determined by operating system.

Within each file are 15 "key-indexes" (this number can be increased by recompiling the source code). Each key-index functions as a logically separate index, even though the information is physically stored in one operating system file. For example:

 

Index 1 Index 2 Index 3 .... Index 15

 

Each key-index contains a variable number of "entries". The maximum number of entries is determined by available space on the disk. For example:

 

Entry 1 Entry 2 Entry 3 .... Entry n

 

Each entry is made up of three parts:

 

Key Record Number Data

 

The default Key type is a zero-terminated string. It may also be other types if the file has been created with an Indexlist. The maximum key length is 240 bytes. The Record Number is a signed long integer. The Data portion of the entry is a sequence of bytes that may vary in length from 0 to 30K bytes. The length of the data portion of the entry is a function parameter specified by the program when adding or changing the entry. This length is automatically stored in the entry as an int type variable.

Entries are sequentially positioned in the file according to the ASCII value of the key and the numeric value of the record number.

Key Indexes

Logically speaking, it is as though there are up to 15 index files open for each physical file open. Each file contains 20 key-indexes that are processed separately, although the keys for each "key-index" reside in one physical index file. The last five indexes are reserved for system use.

Having 15 logical indexes available in a single file allows the programmer to develop complex key and data storage structures without having multiple files open. Any key or data information that is related to the application can be combined in a single file. To the application user, the entire structure would appear to be one file in the disk directory, facilitating the copying and deleting of the file.

The programmer has the option to use separate physical files with one key-index in each file, or multiple key-indexes in a single file.

Entries

The actual items of information stored in the index are referred to as "entries". Each entry consists of three parts:

Key
The key value that is used for ordering and retrieving the entries. Entries are always ordered by key value which depends on the type of key defined at file creating time. The default key type is zero terminated ASCII string (compared using the strcmp C library function).
Data Record

Number

 

A number that is used in two ways: first, as the record number of the data record residing in the data file, and secondly, as a method of resolving duplicate keys. The Data Record Number is appended to the end of the Key value in determining the ordering sequence in the index. Thus if two keys have the same key value, their order in the index is determined by the ascending numerical value of their respective Data Record Numbers. C-Index/II does not allow two entries to have both the same key and the same data record number.

 

 

Data
The part of the entry that may be used to store data. Any data may be stored in this part of the entry up to a length of 30,000 bytes. No assumptions are made about the nature of this data; therefore, it does not have to be a null terminated string or ASCII characters. The length of the data is specified in the dlen parameter and can vary from entry to entry. After locating an entry, the parameter structure variable psp->retlen specifies the size of the data value found. See the psp->dlen and psp->retlen parameter structure descriptions below.

The Parameter Structure

The parameter structure contains two parts: file information variables and parameter variables. File information variables are items of information from the file header that control usage of the file space. These should not be changed in any way by the application program. Parameter variables can be accessed to communicate to and from the C-Index/II operations.

Parameter Structure Members

key
char *key;

The pointer to the character key string. After a C-Index/II operation, this will point to the current key. With add and change operations, this will point to the key just added. With find operations, this will point to the key found or a null key when nothing is found. With delete operations, it will point to a null string.

rec
long rec;

The record number associated with the key. This number can be used for whatever purpose desired. In some cases it is used to reference an actual data record. It is important to remember that the rec variable is a piece of identification related to the current key. It is used to resolve duplicate keys in searches. Keys with both the same key value and record number are not allowed. For more details on duplicate keys, refer to the section "Duplicate and Unique Keys".

data
char *data;

Pointer to the data associated with the current key. As stated, the data can be in any format. The variable psp->dlen is used to determine the length of data actually found. The maximum size of the data record is 30,000 bytes.

dlen
int dlen;

The length of the data value read by the single-key routines (such as cfind, cfirst, etc.). On return from any single-key read function the length of the data value located will be returned in the parameter structure member dlen. In addition, retlen will be set to the length of the data actually returned in the data buffer (see below). Normally dlen and retlen will be the same size. If dlen is larger than retlen, the data record has been truncated on return from the function to make it fit into the data buffer. Also see comment below, "Record Length Information."

retlen
int retlen;

Operations that find entries in the index will return the length of the data portion that was located. If the psp->retlen value is equal to the psp->dlen data length specified in the find function call, the entire data portion was returned to the calling routine. However, if the psp->dlen is larger than the psp->retlen value, part of the located data was lost. C-Index/II truncates the data returned in this event. Since C-Index/II cannot know what type of data is in the record there are no modifications made to the data after it is truncated to make it usable by the application (such as adding a null terminator if the data were a string). Also see comment below, "Record Length Information."

Record Length Information

If a data length parameter value of zero is specified when reading entries, no data will be read and the return value of both dlen and retlen are invalid and must be ignored. To obtain the correct length of a record without reading the entire record, read data buffer length parameter must be set to at least 1. For example, cfind will not set psp->dlen with the following parameters:

 

cc = cfind(psp, 1, "SMITH", 0L, NULL, 0);

 

However it will return the length with the following call:

 

char tmpbuf;

cc = cfind(psp, 1, "SMITH", 0L, tmpbuf, 1);

 

 

Duplicate and Unique Keys

To allow the greatest flexibility in using C-Index/II, key values are always assumed to be duplicate. That is, the application program may add entries when the key portion is the same as an entry already in the index. The order of entries is always determined by the ASCII collating sequence of the key combined with the data record number. For entries in which the keys are identical but the data record numbers vary, the entry order is resolved by the increasing numerical value of the data record numbers.

Record Numbers

When the data is actually being stored in the entry by using a duplicate key, it is necessary for the application program to supply a unique data record number to resolve the problem of duplicate keys. This is accomplished with the cnextrec function which gets a unique long int number from the index header control information. The number maintained in the header is incremented by one each time cnextrec is called. This allows for a very large set of unique numbers. For example, it will take over 100 years to use up the full range of unique numbers in the signed long integer, assuming the extreme case of adding a new entry an average of once each second, 24 hours per day, 365 days per year.

The file create functions (bcreate, ccreate)automatically sets the next record number that will be returned by cnextrec to 1. This number can also be set to any desired value using the csetrec function.

File Operations

The file create functions (bcreate, ccreate, etc) function creates a new C-Index/II file which contains two special data structures: the header record and one empty "node" for adding the first entry. The file create functions must be used to create these two structures in the new file before any information can be entered.

The file open functions (bopen, mopen, copen) open an existing C-Index/II file. The close functions (mclose, cclose) close an open C-Index/II file.

Current Entry Pointer

C-Index/II internally maintains a current entry pointer for each key-index. Whenever the program has performed an operation that either finds an entry or adds a new entry, the current pointer for that index is updated to point to the key that has been found or added. The current key pointer is used to determine where to find the next or previous key, and is also used with the operations that work with the current entry (see Using Current Entries).

Creating a New Index File

New index files are created using the bcreate operation. For example:

 

CFILE file1; /* declare parameter structure 1 */

int cc; /* code from create */

 

cc = bcreate(&file1, "file1.dat" EXCL, NATIVEMODE, NULL)

 

The name of the file is any valid file name that is permitted by the compiler create file function (see the compiler specific code and your compiler reference manual). If any error occurs in the low level create function (including invalid file names), the bcreate function returns a FAIL condition code value. If the bcreate function is successful, it returns an CCOK condition, and the file is now open so that indexing operations may be performed.

Opening an Existing Index File

To use a file that has previously been created and closed, the bopen operation must be used. For example:

 

CFILE file1; /* declare parameter structure 1 */

int cc; /* condition code from open */

 

cc = bopen(&file1, "file1.dat", EXCL, CRDWRITE, NULL);

 

If any error occurs in the bopen function (including invalid file name), the function returns a FAIL condition code. If the operation is successful, the function returns an CCOK condition code. Once the file has been successfully opened, indexing operations may be performed.

Closing an Open Index File

To close the file after processing the mclose operation is used. For example:

 

CFILE file1; /* declare parameter structure 1 */

int cc; /* condition code from close */

 

/* open file for processing */

cc = bopen(&file1, "file1.dat", EXCL, CRDWRITE, NULL)

.

.

.

cc = mclose(&file1); /* close file after processing */

 

It is important that the close operation be performed after using the file. With the default C-Index/II settings and normal operating systems behavior, information will not be lost. However, with optional C-Index/II settings file information may be buffered. Also, some operating systems will buffer information incorrectly in memory until a close operation is performed. If the application program terminates before the mclose function is performed, the index may lose both recently added information and previously added information. For more information on this subject, refer to Chapter 5, "PowerFail Protection".

Adding and Updating Entries

When a new index file is created, it is automatically initialized with 20 empty indexes. The first 15 indexes are available to the application. After creating the file, entries can be added to these indexes. The last 5 indexes are reserved for internal use.

Add a New Unique Entry

cunqadd(psp, keyn, key, rec, data, dlen)

 

This operation adds a new key to the index, but note that duplicate keys are not allowed. If an exact match with the key is encountered a condition code of FAIL is returned.

Add a New Duplicate Entry

cdupadd(psp, keyn, key, rec, data, dlen)

 

This operation also adds a new key to the index, but duplicate keys are allowed. The addition of a duplicate key will always succeed unless there is also a matching record number for the duplicate key or some system type failure (such as a disk full condition). Duplicate key entries are arranged in the index according to the record number (in increasing order). When performing a find operation using a key that has duplicates in the index, the entry with the lowest record number will be encountered first.

Change an Entry

cchange(psp, keyn, key, rec, data, dlen)

 

This operation causes a change in the data portion of an existing entry. If an exact match (including Data Record Number) is not found, a condition code of FAIL will be returned.

Change the Current Entry

cchgcur(psp, keyn, data, dlen)

 

This operation also causes a change in the data portion of an existing entry. The entry that is changed is the current entry that has been found by a key locating operation. If there is no current entry, a condition code of FAIL is returned. For more details about current entries, refer to the section on "Finding Entries".

Save an Entry

csave(psp, keyn, key, rec, data, dlen)

 

The csave function will add a new entry to the index if the entry does not already exist. However, if an exact match is found, that is, both Key and Data Record Number match, the data portion of the entry will be changed to the passed data value. This allows the application program to perform an add or a change with a single function call without having to test for the existence of the entry first.

Finding Entries

Several operations allow the application program to find entries within the index. Entries may be found by both the key value and by relative position in the index. The entry location operations return two items of information:

 

Key Found
The key returned by the operation is located in a storage space reserved in the parameter structure. A pointer to this key is located in the parameter structure variable key.
Data Located
The calling function passes a pointer to the location where the data record is to be returned. The maximum size of the data storage area is indicated by the dlen parameter. The data record information is automatically returned to the calling function in the indicated data space up to the length specified. Provided that a buffer length greater than zero is specified, the actual size of the data in the entry is returned in the parameter structure variable psp->dlen and the length of the data placed in the application supplied buffer is returned in the variable psp->retlen. If a zero length buffer is specified, the variables psp->dlen and psp->retlen are invalid and should be ignored.

 

Find an Entry by Key Value

cfind(psp, keyn, key, rec, data, dlen)

 

This operation performs a search through the index to find an entry that has a key of equal or greater positional value than the specified key. A condition code is returned by the function call indicating the result of the search:

 

FAIL
An entry was not found.
CCOK
An entry was found with an exact match of key and record number.
KEYMATCH
An entry was found with a matching key, but the data record number of the found entry is greater than requested. This return code is useful when the application program is trying to find the first entry in a series of entries all of which have the same key value. (For example, the program may find the first of several entries, each with the name "SMITH" for their key value.)
GREATER
An entry was found which does not match the key value, and instead, has a key of the next greater positional value.

 

Find the Next Entry

cnext(psp, keyn, data, dlen)

 

This operation finds the next entry in the index after the location of the current pointer. The current pointer is then updated to the new location and the entry information is returned to the calling routine. A condition code is returned to indicate the result:

 

FAIL
Indicates that no records exist in the index beyond the location of the current pointer.
CCOK
Indicates that no errors were encountered, and the next key in the index was found.

 

Find the Previous Entry

cprev(psp, keyn, data, dlen)

 

This operation finds the entry located in the index before the current pointer. The current pointer is then updated to the new location and the entry information is returned to the calling routine. A condition code is returned to indicate the result:

 

FAILIndicates that no more records exist in the index previous to the location of the current pointer.
CCOK
Indicates that no errors were encountered, and the previous key in the index was found.

Find the First Entry

cfirst(psp, keyn, data, dlen)

 

This operation finds the first entry in the index. The current pointer is then updated to the new location and the entry information is returned to the calling routine. A condition code is returned to indicate the result:

 

FAIL
Indicates that there are no records in the index.
CCOK
Indicates that no errors were encountered, and the first key in the index was found.

 

Find the Last Entry

clast(psp, keyn, data, dlen)

 

This operation finds the last entry in the index. The current pointer is then updated to the new location and the entry information is returned to the calling routine. A condition code is returned to indicate the result:

 

FAIL
Indicates that there are no records in the index.
CCOK
Indicates that no errors were encountered, and the last key in the index was found.

 

Find the Current Entry

cgetcur(psp, keyn, data, dlen)

 

This operation finds the current entry in the index. The location of the current pointer is not changed, and the entry information is returned to the calling routine. A condition code is returned to indicate the result:

 

 

 

FAIL

Indicates that no record in the index has yet been located (i.e., the current record has not been set).
CCOK
Indicates that no errors were encountered, and the current key in the index was found.

 

Deleting Keys and Data

Entries that have been added to an index may also be deleted. The cdelete operation locates the entry to delete by automatically calling the cfind function before deleting the key. The cdelcur operation deletes the entry indicated by the current pointer that was set by a previous operation.

After a successful delete operation, the current entry pointer indicates where the entry existed before deletion. However, because an entry no longer exists at this location, operations that work with the current entry (such as a subsequent cdelcur) will return a FAIL condition code.

A typical usage of cdelcur in an application program is to use cnext or cprev in combination with cdelcur to delete a series of entries located together in the index.

The delete operations are as follows:

Delete Entry by Key Value

cdelete(psp, keyn, key, rec)

 

In this operation, the entry is found by using the key and record number values passed. If an exact match is not found an error is returned. If there are duplicate entries (identical key and data record numbers), the first entry in the duplicate sequence will be deleted. Care should be used when deleting duplicate records.

This function returns the following condition codes:

 

 

FAIL
The specified entry was not found, and no deletion occurred.
CCOK
The entry was found and deleted.

 

Delete Current Entry

cdelcur(psp)

 

In this operation, the current entry is deleted. If a current key does not exist, no deletion occurs. This function is useful when the application program finds a key before deleting it. In this case, the entry does not need to be relocated. This is especially important when using duplicate key entries.

The function returns the following condition codes:

 

FAIL
There is no current entry, and no deletion occurred.
CCOK
The entry was found and deleted.

 

Tutorial Program for Single-Key Functions

The C-Index/II Tutorial demonstration program is designed to allow you to try C-Index/II operations, and view the results without having to do any programming.

Starting the Tutorial

This demonstration program is called tutor.exe and is located on the utilities disk. It was designed to be used with an IBM or IBM compatible computer. If you are using another type of system, refer to the documentation on the disk for more information on using the tutorial on your system. The Tutorial uses 2 files that are supplied with the system: tutora.ind and tutorb.ind. These files have been initialized for this demonstration.

To run the Tutorial type at the operating system prompt:

TUTOR <return>

 

Tutor will automatically begin execution, and will respond with a display. The screen will show a list of commands in upper case, including parameters that may be required for each command. At the bottom of the screen will be:

 

Enter Function :

 

Although Tutor will accept lower case commands, for the sake of simplicity, only upper case characters are used in these examples.

Most of the commands are self-explanatory. To begin, however, you must open a file. Tutor uses two psp's, and you can switch between them with the CHGPSP command. There is always one current psp, and any operations use this current psp. Tutor starts with psp 1 current, so unless you begin by changing the active psp, the first open will open tutora.ind and will use psp 1. To perform this, type:

OPEN <Return>

 

If you want to open the other file, you must first switch to psp 2 with the CHGPSP command and then execute another open. Tutor will sense psp 2 and open tutorb.ind.

To get the menu again, type:

HELP <return>

 

HELP may be typed at any time between operations. Do not type HELP in the middle of a command, as it will be interpreted as a parameter value for that operation. If in doubt about an operation, erase the entire command line using backspace, then type HELP. The help information will also be displayed when you press return on a blank entry line.

The functions that expect parameters should be separated by a space. For example, the unique add operation requires the parameters to be indicated in the following format:

UNQADD <key> <rec> <data>

 

To add a unique entry with the key value "ABC", a record number of 13, and the data string "ABC Data", type:

UNQADD ABC 13 ABC Data <Return>

 

Note that the last parameter, data, can contain spaces. For any operation with data, the data is expected as the last parameter, and is read to the end of the command line.

You are encouraged to experiment with this tutorial. There is also a file tutora2.ind that is a duplicate of tutora.ind. You can use tutora2.ind to restore the file to its original state.

You should refer to the reference section on the individual functions while using the tutorial in order to understand what the functions are supposed to do. Be sure to close both psps before exiting so your work will be saved.

To exit the tutorial type:

BYE <Return>

 



C-Index/II Home Page

www.triosystems.com © Copyright 1996 - 1999 Trio Systems LLC

C-Index/II User Guide © Copyright 1983-1997 Trio Systems LLC

User Guide Revision Date: 5/2/96

All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form or by any means, electronic, mechanical, photocopying, recording, or otherwise, without the prior written permission of Trio Systems LLC.

Trio Systems is a registered trademark of Trio Systems LLC. C-Index, C-Index/II, SpeedRead, ReadShare and PowerFail Protection are exclusive trademarks of Trio Systems LLC.