| |CI2 Home|Literature|Support|Documentation|Y2K|Order|License| | |||
| Chapter 8 Advanced Key Types
C-Index/II supports all types of keys, in addition to string keys. This chapter provides information about how to use binary, numeric, and segmented keys. A wide variety of key types are supported. The collating sequence for these types is controlled by the compiler specific C-Index/II code. This makes it easy to modify the key behavior for special requirements, such as structures used as keys, or strings using foreign language character sets.
The default key type is a C style null terminated string. In order to use any other type of key, it is necessary to tell C-Index/II what type is being used. A special structure, the "indexlist" (NDXLIST), is used to specify the index types. The indexlist structure is defined as follows:
/* index information list structure */ typedef struct _NDXLIST { unsigned char keyn; /* index number */ unsigned char keytype; /* type of key stored in the index */ KEYSEGLIST PDECL *segptr; /* segment pointer */ } NDXLIST;
For example, this is how to specify storing integer key values in index 1:
NDXLIST indexlist[2]; /* index type info */
/* set up list of index types (one index of type int)*/ indexlist[0].keyn = 1; /* index 1 */ indexlist[0].keytype = INTIND; /* integer index */ indexlist[0].segptr = NULL; /* not segmented key, set to null */ indexlist[1].keyn = ENDNDXLIST; /* terminate indexlist */
Another example, a file containing three indexes, a case-insensitive string key, a double key, and a segmented key:
NDXLIST indexlist[4]; /* index type info */
/* set up list of index types (one index of type int)*/ indexlist[0].keyn = 1; /* index 1 */ indexlist[0].keytype = NOCASEIND; /* case-insensitive string index */ indexlist[0].segptr = NULL; /* not segmented key, set to null */ indexlist[1].keyn = 2; /* index 2 */ indexlist[1].keytype = DOUBLEIND; /* double floating point index */ indexlist[1].segptr = NULL; /* not segmented key, set to null */ indexlist[2].keyn = 3; /* index 3 */ indexlist[2].keytype = SEGIND; /* segmented key index */ indexlist[2].segptr = seglist; /* ptr to keyseglist for segmented key */ indexlist[3].keyn = ENDNDXLIST; /* terminate indexlist */
The index list is variable in length, and is terminated with the special value ENDNDXLIST assigned to the last keyn variable of the list. The indexlist is normally used to specify index types at file create time, using bcreate or dbcreate. If no entries have been added to an index, it is also possible to change the type of an index at a later time using the cchgndx function. When opening a file containing non-string keys, the indexlist must also be passed to the bopen or dbopen functions. The key pointer always points to the first byte of the key, regardless of the type. In other words, all key values are passed to C-Index by reference (address), not by value. For example, a pointer to an integer key value can be created like this:
int xyz; /* search key value */ void *keyptr; /* pointer to key */
keyptr = &xyz; /* pointer to first byte of key */
Two string types are supported, STRINGIND and NOCASEIND. These are both null terminated C strings. The STRINGIND type defaults to a collating sequence provided by strcmp in the C library. The NOCASEIND type defaults to a collating sequence provided by stricmp in the C library (ignore case). If no index type is specified in an indexlist, it defaults to STRINGIND. It is not necessary to specify indexes that are of type STRINGIND, although it is a good idea to do so for documentation purposes. Key values using variable length binary information can be specified using the following types:
All variable length binary keys have the same format, a single byte specifying length (excluding the length byte itself), followed by a variable number of bytes. A single byte defines the length, which therefore cannot exceed 255 bytes. This limitation is consistent with the general limitation that keys can never be longer than MAXKLEN, which is always smaller than 255. When using multi-key routines, binary field lengths are specified by the length of the field in the datalist. C-Index/II automatically converts the binary field to the correct key format (with one byte for length). If the length exceeds MAXKLEN, the error BADSTRLEN will be returned by dadd or dupdate. The default collating sequence of binary keys is performed by the memcmp function from the C library . This does an unsigned bytewise comparison starting at the first byte in the sequence. If you wish to change the collating sequence of binary keys, you must use a custom binary key. The collating sequence for your special key type can be established by modifying the compiler specific definition of the functions cikeycmp and custincrkeyval (for custom binary keys). A single fixed length binary key value is supported by C-Index/II. The FIXIND key type and FIXFLD field type indicate a binary value of a fixed size. The length and collating sequence of the value is determined at library compile time by the compiler specific code. The default length is 6 bytes with a binary collating sequence. Fixed binary indexes are especially useful for indexing on an application specific complex data type. For example, an application may assign a time to records based on a structure that contains a two byte time code and four byte date code. By supplying the correct size and key comparison logic in the compiler specific C-Index/II code, the application has the possibility of a wide variety of key types. Modify the cikeycmp and custincrkeyval functions in the compiler specific C file, and the FIXINDLEN define in the compiler specific header file. The standard C language numeric types are supported for indexing:
When creating files for use across multiple platforms, it may be necessary to avoid the INTIND type, since different compilers may define this as either 2 bytes or 4 bytes. All other numeric types are automatically converted between computers. This conversion process has been tested for all ports provided with C-Index/II. If you port the library to another platform, you will need to test this feature using test6 and test7. The following example adds and finds a single-key entry using an integer key value. The same approach can be used with any numeric data type. CFILE file1; /* file parameter structure */ int xyz; /* search key value */ long rec; /* search record number */ int cc; /* return condition code */
int fndkey; /* value of key found */ NDXLIST indexlist[2]; /* index type info */
/* set up list of index types (one index of type int)*/ indexlist[0].keyn = 1; /* index 1 */ indexlist[0].keytype = INTIND; /* integer index */ indexlist[0].segptr = NULL; /* not segmented key */ indexlist[1].keyn = ENDNDXLIST; /* terminate indexlist */
cc = bcreate( &file1, "file1.ind", SHARED, INTELMODE, indexlist);
/* add integer key to file using cdupadd */ xyz = 1234; rec = 0; cc = cdupadd(&file1, 1, &xyz, rec, NULL, 0); if (cc != CCOK) printf("Error adding record: %d\n", cc);
/* find single key using integer */ xyz = 1234; rec = 0; cc = cfind(&file1, 1, &xyz, rec, NULL, 0); if ((cc != CCOK) && (cc != GREATER)) { printf("Error finding record: %d\n", cc) } else { #ifdef REQUIRES_WORD_ALIGNMENT /* 68000, RISC, etc */ /* get key value from psp using block move */ memcpy(&fndkey, file1.key, sizeof(int)); #else /* no word alignment problem for PC computers */ /* get key value from psp using assignment */ fndkey = *((int *)file1.key); #endif printf("Found Key: %d\n", fndkey); }
This call to cfind returns the first integer key value greater than or equal to 1234. The actual key value located by the search will be returned in the variable file1.key. You may need to copy the file1.key value to a word aligned variable if you are using a computer that addresses all integer values on word aligned boundaries (such as 68000, and RISC computers). The define REQUIRES_WORD_ALIGNMENT will be set in the compiler specific C-Index header for compilers requiring this type of processing. Segmented keys (also called concatenated keys in some data managers) allow an application to create a key that is made up of several fields. C-Index/II supports segmented keys in single-key and multi-key routines. Segmented keys are variable in length overall, and segments may be of differing lengths and types. Unlike other data managers, there is no requirement to add padding bytes to the key to make the individual segments fixed in length. Instead, C-Index/II keeps track of each segment separately and stores only the number of bytes needed for that value. To store segmented keys in an index, the application must pass segment information to the dbcreate, dbopen, bcreate or bopen functions via the indexlist. The indexlist information indicates to the system that an index will be using a segmented key and points to a "keyseglist" structure array (KEYSEGLIST) which specifies each of the key segments. A structure array, called the "keyseglist", is used to specify the segments to be used in a key. The indexlist "segptr" variable for the segmented key will point to this segment list. The definition of a key segment list is as follows:
/* key segment information list */ typedef struct { /* keytype used for single-key and multi-key segmented keys */ unsigned char keytype; /* type of key stored in the index */
/* fldoff and maxlen used for multi-key segmented keys only */ int fldoff; /* datalist offset of field storing key value */ unsigned char maxlen; /* max key field length (ignored is set to 0) */
/* order for sorting this key segment */ unsigned char descending; /* 0 = ascending order, 1 = descending order */
/* reserved for future use */ void PDECL *reservedinfo; /* info list - reserved for future use */ } KEYSEGLIST;
The variable keytype is one of the supported index types, such as STRINGIND, FIXBININD, SHORTIND, etc. C-Index automatically constructs key values for multi-key records by extracting data from fields in the datalist. The variable fldoff is the structure array subscript ("field offset") in the datalist for the multi-key field to be used for this key segment. It is only used for multi-key routines. When using single-key routines, fldoff will be ignored. The variable descending indicates the sort order of this key segment. If it is 0 (FALSE), the key sorts in the conventional ascending order. If it is 1 (TRUE), this segment will sort in reverse order, i.e., the largest key value will be first in the sequence. The last variable reservedinfo is reserved for future use, and should be set to NULL.
Segmented keys are essentially special binary keys. The format is a one byte length indicator (unsigned char) followed by each key segment value stored end-to-end. All of the segments specified for a key must be in keys passed to the system. The length byte specifies only the length of the information, excluding the length byte itself. In other words, the total length of a key including the length byte is one greater than the length byte value. C-Index/II uses the indexlist and keyseglist information supplied during file create or open to understand the key collating sequence. An example single-key keyseglist and indexlist could look like this:
/* list describing a segmented key */ KEYSEGLIST seginfo[] =/* seglist specifying key segments */ { /* keytype fldoff maxlen order reserved */ { STRINGIND, 0, 0, 0, 0 }, { SHORTIND, 0, 0, 0, 0 }, { LONGIND, 0, 0, 0, 0 }, { ENDSEGLIST,0, 0, 0, 0 } };
/* list describing index types */ NDXLIST indexlist[] = { /* keyn keytype segptr */ { 1, SEGIND, seginfo }, { ENDNDXLIST, 0, 0 } };
This describes index number one as a segmented key consisting of three segments: a string, an integer, and a long. All segments are sorted in ascending order.
When using the single-key routines, the application program must create the correct key format for all index operations. The following code illustrates one way of building a key for searching this example segmented key:
char searchkey[MAXKLEN]; char *stringval = "Smith"; /* string segment value of search key */ short shortval = 1234; /* short segment value of search key */ long longval = 87654321L; /* long segment value of search key */ int keylen;
keylen = strlen(stringval) + 1; /* length of string key segment */ if (keylen > MAXKLEN - 7) { printf("String value too long"); /* key cannot exceed length of MAXKLEN */ } else { strcpy(searchkey+1, stringval); /* copy in string search value */ /* copy in short search value */ memcpy(searchkey+1+keylen, &shortval, sizeof(shortval)); keylen += sizeof(short); memcpy(searchkey+1+keylen, &longval); /* copy in long search value */ keylen += sizeof(long); *searchkey = (char)keylen; /* set overall length of key */ }
An example multi-key keyseglist, indexlist and datalist could look like this:
KEYSEGLIST seginfo[] = { /* keytype, fldoff, maxlen, order, reserved */ { STRINGIND, 0, 0, 0, NULL }, { NOCASEIND, 1, 4, 0, NULL }, { INTIND, 7, 0, 0, NULL }, { ENDSEGLIST, 0, 0, 0, NULL } };
NDXLIST indexlist[] = { /* keyn , keytype, segptr */ { 1, STRINGIND, NULL }, { 2, SEGIND, seginfo }, { 3, LONGIND, NULL }, { 4, DOUBLEIND, NULL }, { ENDNDXLIST, 0, 0 } };
DATALIST exampledl[] = { /*ftyp ktyp dup len index ptr*/ {STRINGFLD, STRINGFLD, UNQKEY, 20, 1, stringval1}, {STRINGFLD, NONKEY, NONKEY, 20, 0, stringval2}, {STRINGFLD, NONKEY, NONKEY, 20, 0, stringval3}, {STRINGFLD, NONKEY, NONKEY, 20, 0, stringval4}, {STRINGFLD, NONKEY, NONKEY, 20, 0, stringval5}, {BINARYFLD, NONKEY, NONKEY, 50, 0, binval }, {SEGFLD, SEGIND, DUPKEY, MAXKLEN,2, NULL }, {INTFLD, NONKEY, NONKEY, 2, 0, &intval }, {LONGFLD, LONGIND, DUPKEY, 4, 3, &longval }, {FLOATFLD, NONKEY, NONKEY, 4, 0, &floatval }, {DOUBLEFLD, DOUBLEIND, DUPKEY, 8, 4, &doubleval}, {ENDLIST, 0, 0, 0, 0, NULL} };
Note that the reserved variable of KEYSEGLIST is always NULL (a null pointer). This field is reserved for use in future versions of C-Index/II. When using the multi-key routines, C-Index takes care of creating index values and maintaining them in the database file. For example, when the application adds a record:
cc = dadd(&file1, exampledl);
C-Index/II will build a variable length data record based on the datalist. Then it looks at the datalist to determine if any fields need to be indexed. In this example, there are four indexes of string, segmented, long, and double keys. The string, long, and double key values are copied directly from the record, and added to their respective indexes. The segmented key is also automatically built. The SEGFLD field indicates a segmented index field. This is a virtual field. The values for the key are not stored in the record, since the values are already in the record in other fields. C-Index/II examines the indexlist for index 2 to locate the keyseglist. The keyseglist then indicates which fields in the current datalist are combined to create the segmented key. In this example the fields at offsets 0, 1, and 7 are combined (stringval1, stringval2, and intval). C-Index/II now builds the segmented key and adds it to index 2. The application program must create the correct key format for all index retrieval operations. This can be accomplished by building the key manually, as described in the previous section about single-key usage. Alternatively, the dsetkey function will build a key value based on a datalist pointing to the segmented key values desired. The following code illustrates building a key using dsetkey with the above datalst1: char searchkey[MAXKLEN]; char *stringval1 = "Smith"; /* string1 segment value of search key */ char *stringval2 = "Bob"; /* string2 segment value of search key */ long longval = 87654321L; /* long segment value of search key */
/* build segmented key for index #2 */ cc = dsetkey(&file1, 2, exampledl, searchkey);
To determine what segmented key value has been read from multi-key search operations (dfind and dseq), the application must parse the value returned in the variable psp->key. This is essentially the reverse of manually building the key: char stringres1[MAXKLEN]; /* string segment value of found key */ char stringres2[MAXKLEN]; /* string segment value of found key */ long longval; /* long segment value of found key */ char *keyptr; /* work pointer to each segment */ int keylen; /* length of string segments */
keyptr = (char *)psp->key; /* found key is in psp */ keyptr++; /* skip over length byte */
strcpy(stringres1, keyptr); /* extract string1 segment value */ keylen = strlen(stringres1); /* length of string key segment */ keyptr += keylen + 1; /* point to next segment */
strcpy(stringres2, keyptr); /* extract string2 segment value */ keylen = strlen(stringres2); /* length of string key segment */ keyptr += keylen + 1; /* point to next segment */
memcpy(&longval, keyptr, sizeof(long)); /* copy in long segment value */
C-Index/II Home Pagewww.triosystems.com © Copyright 1996 - 1999 Trio Systems LLC |