Developer Guide |
|
FairCom ISAM for C |
|
Audience: |
Developers |
Subject: |
ISAM and Low-Level API Functions and Basic Concepts |
Copyright: |
© Copyright 2024, FairCom Corporation. All rights reserved. For full information, see the FairCom Copyright Notice. |
FairCom DB® Professional is the complete environment for developing applications using the sophisticated FairCom DB Advanced Core Engine. Written in the C language, FairCom DB file management integrates with your C/C++ applications to provide easy-to-use, yet flexible data file manipulation and indexing. FairCom DB is designed to be used in a variety of operating system environments.
The significant features of FairCom DB are:
This guide serves both as an introduction to FairCom DB for the new user and as a reference guide for the experienced user.
This manual assumes you are already familiar with the C programming language. If you are not familiar with these concepts, you should develop a reasonable proficiency with the language for effective usage of FairCom DB.
This section of the reference guide describes a number of conventions that we use in relation to the C language, as well as a few C language concepts that we believe are worth reviewing. In this section:
Data Type Conventions
The actual representation of a data type declaration may vary from one operating environment or processor type to another. For instance, on one system the int data type may be represented by a 2-byte integer, while on another system int may be represented by a 4-byte integer. Our goal is to make a product using FairCom DB as portable as possible. Differences like these between systems can interfere with that goal. We want to ensure that a 2-byte integer in FairCom DB will be a 2-byte integer on any system. Therefore, we define our own data types as shown below. By carefully using these data types, you guarantee that a program operates consistently across all supported operating environments.
The following data types are used throughout the FairCom DB routines called by your application programs. C language typedefs have been included in the c-tree header files. Most of these will be found in the header file ctport.h.
We recommend that you use these conventions in your products, as well. By carefully avoiding the use of native variable types in your variable declarations you can create an application that is very easy to port from one compiler, or operating environment, to another
COUNT
COUNT parameters are used to pass values such as key length or file number to c-tree functions. Most c-tree functions return error codes of type COUNT. This is a 2-byte signed integer.
UCOUNT
UCOUNT parameters are used to pass fixed length data record lengths and file extension sizes to the c-tree functions. This is a 2-byte unsigned integer.
LONG
LONG values represent data record positions. This is a 4-byte signed integer.
ULONG
This is a 4-byte unsigned integer.
TEXT
Character pointers to type TEXT are used to pass file names and key values to the c‑tree routines.
IFIL
Incremental ISAM structure. IFIL structures contain the parameters necessary to create, open, and close ISAM files without a parameter file. The ISAM parameters are discussed in ISAM Functions.
DATOBJ
The DATOBJ structure is the data type used in the Data Object Definition Array or DODA, which defines the record layout to c-tree. See Record Schemas for additional information.
VRLEN
VRLEN is used to pass or receive a record length and buffer sizes.
UINT
UINT are native system unsigned integers.
VOID
VOID is used in two ways: to define functions that return no value, and to define a generic pointer. Some compilers do not support the declaration void, and others do. In ctport.h you will find a define similar to the following:
#define void void
If your compiler does not support the void declaration, substitute the following;
#define void int
VOID pointers are better than character or TEXT pointers when you are passing a variable address to a function, and you don’t know if you are going to use a character string or a structure. A VOID pointer is a generic pointer, and if you are using function prototyping, it will let you use any type of variable pointer. A TEXT pointer will give you a warning when you compile a program passing a structure address.
p TYPES
The lower case p indicates this data type is cast to be a pointer. For example, pCOUNT is equivalent to (COUNT *). Most of the data types defined by c-tree have companion types prefaced with a lower case p (e.g., pVOID, pTEXT, . . .).
Strings
The C language has no formal data type for character strings. All string manipulation is done by processing arrays of characters. The C language provides no automatic checking on the boundaries of an array. These features can be exploited in many cases, but when it comes to string manipulation they present hurdles to overcome.
When you are copying a character string from one variable to another, it is up to the programmer to ensure that the receiving variable has enough room to hold the entire string being copied. If you are not careful, it is possible for the string being copied to overlay an adjacent variable in addition to the target, with unpredictable results. This may be the most common error that can occur in a C program, and it can be very difficult to trace.
To complicate matters, many string manipulation operations performed in a FairCom DB program are NOT working with traditional null-terminated character strings. As we will see later, some types of c-tree key values may have null characters embedded in the middle. In addition, copying data structures from one location to another is handled as a string manipulation, and the data structure will have a number of null characters embedded in it.
To assist in working with strings and structures, c‑tree includes a copy function, cpybuf(). c‑tree redefines cpybuf() to memcpy() in those environments which support this ANSI function. If not, cpybuf() is defined as:
void cpybuf(destination, source, length)
pTEXT destination;
pTEXT source;
UINT length;
This function copies length bytes from source to destination. It copies strings, structures, or buffers. It will NOT stop when it sees a null character in source. cpybuf() stops when it has copied length bytes. We strongly suggest using this function when you know the length of the buffer or string you are copying.
FairCom DB also includes cpybig(), which provides the same functionality as cpybuf(), but works for very large buffers even on machines with segmented memory models. The length parameter is declared as VRLEN instead of UINT, which provides for larger buffers.
V11.8 and later added JSON as a new data type, called CT_JSON. It is physically stored in a table using the CT_4STRING format, which can support strings up to 2 GB in length.
It is available in all APIs with varying levels of support: SQL, NAV, ISAM, and Low-Level.
The NAV API allows you to index specific JSON properties using FairCom’s path language. This allows you to find records containing specific property values or ranges of property values. And it allows you to navigate records in sorted order using JSON property values.
In SQL and ISAM, you can read and write the string value stored in a JSON field but you cannot extract the values in JSON properties and treat them as columns. You cannot also use indexes created by the NAV API.
JSON Data Type
To create a table that contains a CT_JSON field, specify CT_JSON as the field type in the DODA. When you add a record that contains a CT_JSON field value, store it in CT_4STRING format, with the first four bytes of the field containing the length of the JSON value, followed by the JSON value.
JSON Indexing
To create an index on a CT_JSON field, set the index attributes in the IIDX and ISEG structures that you pass to CREIFIL(). Before calling CREIFIL(), call the PUTKSEGDEF() function to set the CT_JSON index attributes that will be used for the CT_JSON index in the CREIFIL() call. The CT_JSON index attributes include the key segment length, the c-tree data type of the key value, and the JSON key name that is being indexed.
The PUTKSEGDEF() function can be used to specify CT_JSON index attributes for a call to CREIFIL():
Example
In this example, the supplied IFIL definition (pifil) has two indexes of one key segment each. The example code sets the first extended segment info pointer to NULL and the second to a ctKSEGDEF structure initialized with the CT_JSON index attributes.
ISEG myseg[] = {
{ 8, 8, SRLSEG},
{ 3, 4, SCHSEG}
};
IIDX myidx[] = {
{8, 0, NO, NO, 0, 1, myseg, "$ROWID$"},
{4, 0, NO, NO, 0, 1, myseg+1, "doc_id"}
};
IFIL mydat = {
"qajson",
-1,
20,
0,
ctFIXED | ctTRNLOG,
3,
0,
ctTRNLOG,
myidx
};
ctKSEGDEF sd;
pctKSEGDEF pksegs[2];
/* No extended segment info for first index. */
pksegs[0] = NULL;
/* Set the extended segment info for the second segment. */
memset(&sd,0,sizeof(sd));
sd.kseg_type = ctKSEG_TYPE_JSON;
/* use source type from schema */
sd.kseg_styp = ctKSEG_STYP_PROVIDED;
/* set key segment length */
sd.kseg_ssiz = 4;
/* set c-tree data type of key value */
sd.kseg_comp = CT_INT4;
/* set the json key name that is being indexed */
strcpy(sd.kseg_desc,"id");
pksegs[1] = sd;
rc = CREIFIL(pifil);
The name of the JSON key to index is stored in the kseg_desc field of the ctKSEGDEF structure. However, this field is limited to ctKSEGDLEN (32) bytes in the ctKSEGDEF structure definition. To overcome this limit, the ctKSEGcreidx usage of PUTKSEGDEF() accepts an array of pointers to ctKSEGDEF structures instead of an array of ctKSEGDEF structures so that an application can allocate a buffer to hold the CT_JSON index attributes that is larger than the ctKSEGDEF structure when the JSON key name exceeds the length of the field as defined in the ctKSEGDEF structure. As long as kseg_desc is NULL-terminated, the larger kseg_desc value can be passed to PUTKSEGDEF() in this manner.
The ctKSEGcreidx information that is passed to PUTKSEGDEF() is stored in the c-tree library until the next call to an ISAM level index create function: CREIFIL(), CREIFILX(), CREIFILX8(), PRMIIDX(), PRMIIDX8(), TMPIIDX(), TMPIIDXX(), TMPIIDXX8(). The recommended usage is to call PUTKSEGDEF() with filno of ctKSEGcreidx immediately before the index create call.
For an extended key segment type (kseg_type) of ctKSEG_TYPE_JSON, a call to GETKSEGDEF() is able to return to the caller a larger amount of ctKSEGDEF data than the size of the ctKSEGDEF structure. A call to GETKSEGDEF() for a kseg_type of ctKSEG_TYPE_JSON should set kseg_ssiz to the size of the supplied buffer. If GETKSEGDEF() finds that the data is larger than the supplied buffer size, it returns -VBSZ_ERR and returns the required length in the kseg_rsv1 field. For example:
ctKSEGDEF sd;
NINT rc,bufsiz;
sd.kseg_type = ctKSEG_TYPE_JSON;
sd.kseg_ssiz = bufsiz;
if ((rc = GETKSEGDEF(keyno,i,&sd)) < 0) {
if (rc == -VBSZ_ERR) {
/* Buffer too small. sd.kseg_rsv1 holds the required size. */
}
}
This new behavior of PUTKSEGDEF() and GETKSEGDEF() is intended to be used to send and receive attributes for other extended key segment types in the future.
JSON Error Code
Error code 1122:
JSON_KTFM_ERR: CT_JSON key transformation failed. Check sysiocod for details. See CTJSON_* definitions.
sysiocod values that may be returned in case of error 1122:
#define CTJSON_NOT_JSON -1038 /* Not a valid JSON object */
#define CTJSON_NOT_OBJECT -1039 /* Valid JSON, but not an object */
#define CTJSON_UNSUPPORTED_TYPE -1040 /* The specified key data type is not supported */
Limitations to JSON Key Segments