Product Documentation

c-treeDB API for C++ - Developers Guide

Previous Topic

Next Topic

Custom Application Expressions

Use the conditional expression parser/analyzer to evaluate application-specific expressions. The FairCom DB expression parser is a full standalone feature in and of itself, and can be used directly in your own applications for purposes other than data filters, conditional indexes, or partitioned files. Two core API functions provide a powerful interface into this advanced expression handling.

FairCom DB’s expression parser/analyzer requires two key steps:

  1. Call cndxparse() to parse your expression, producing an expression tree that the expression analyzer later evaluates.
  2. Call cndxeval() to evaluate your expression tree using data from a buffer in memory.

For a complete sample program, see ctexpr.c in the ctree/source directory.

See also:

In This Section

Parsing Expressions

Evaluating Expressions

Previous Topic

Next Topic

Parsing Expressions

Parsing your expression involves three steps:

  1. Define a DODA structure.
  2. Parse the DODA into a record schema and field name list.
  3. Parse your expression to produce an expression tree.

Sample code to perform these steps is shown below. This code assumes FairCom DB has been initialized prior to calling ctparsedoda().

Expression Parsing Example

#include "ctcndx.h" /* For PTREE type */

/* Define a DODA structure. */

DATOBJ doda[] = {

{"CustomerNumber", 0, CT_INT4U},

{"ZipCode", 4, CT_FSTRING, 9},

{"State", 13, CT_FSTRING, 2},

{"LastName", 15, CT_STRING, 37},

{"FirstName", 52, CT_STRING, 37},

{"Address", 89, CT_STRING, 49},

{"City", 138, CT_STRING, 37}

};


COUNT retval; /* Return code. */

pTEXT schema; /* Record schema. */

pTEXT names; /* Field name list. */

PTREE ptree; /* Expression tree. */

pTEXT expr; /* Expression string. */

/* Parse the DODA into a record schema and field name list. */

if ((retval = ctparsedoda(doda, 7, &schema, &names)) != 0)

printf("Error %d parsing DODA.\n", retval);


/* Parse your expression to produce an expression tree. */

expr = "stricmp(LastName, \"Smith\") == 0

&& CustomerNumber > 10000";

ptree = cndxparse(schema, names, expr, strlen(expr));

if (!ptree)

printf("Error: Unable to parse expression.\n");

else

printf("Successfully parsed expression.\n");

To invoke the expression parser to parse a string expression and produce an expression tree, call cndxparse(), which is declared as follows:

PTREE cndxparse(pConvMap Schema, pTEXT Names, pTEXT InputText,

NINT InputTextSize)

Schema is a pointer to a record schema derived from a DODA definition. Names is a pointer to a list of the field names from the DODA. InputText points to a NULL-terminated string expression, and InputTextSize is the length of InputText.

One of the most useful features of the expression parser is its ability to associate symbolic names in expressions with data in a buffer in memory. To use this ability, you must define a record schema, known as a DODA (Data Object Definition Array). A DODA is an array of field specifications, each of which contains a field name, field offset, field type, and a field length. By providing the expression parser with a DODA, you may include references to DODA field names in your expressions. See the sample code shown later in this section for an example of a DODA definition.

While a DODA is conveniently defined in your application using an array of DATOBJ structures, cndxparse() does not take a DODA in DATOBJ form, but instead accepts a record schema and a list of the field names from the DODA. In order to simplify converting your DODA into the required record schema and field name list, FairCom has written a utility function, ctparsedoda(). This function can be found in the sample file ctexpr.c.

ctparsedoda() is declared as follows:

COUNT ctparsedoda(pDATOBJ doda, UCOUNT numfld, ppTEXT ppschema,

ppTEXT ppnames)

Previous Topic

Next Topic

Evaluating Expressions

Having produced an expression tree, you are ready to evaluate the expression using the expression analyzer. To evaluate your expression, call cndxeval(), which is declared as follows:

COUNT cndxeval(PTREE Tree, pVOID Recptr, pConvMap Schema)

  • Tree is an expression tree returned by cndxparse().
  • Recptr points to a buffer containing data you wish to evaluate with your expression.
  • Schema is the record schema ctparsedoda() produced from your DODA definition. The record schema is used to associate data in Recptr with field names specified in your expression.

Note: Before you call cndxeval() the first time, you must ensure that a run-time stack has been allocated for the expression analyzer.

To summarize, evaluating your expression involves three steps:

  1. Allocate a run-time stack for the expression analyzer (first time only).
  2. Set up a buffer containing the field data used in the expression.
  3. Evaluate the expression.

If you wish, you can repeat steps 2) and 3) multiple times. Sample code to perform these steps is shown below. It is assumed the Get_Buffer() routine allocates a record buffer and initializes it with data conforming to the field definitions specified in the DODA.

Expression Evaluation Example

COUNT retcidx; /* Result of expression evaluation. */

pTEXT recbuf; /* Record buffer. */


/* Allocate a run-time stack for the expression analyzer (first time only). */

if (!ctcidxStk) {

ctcidxStk = (pVOID) getcndxmem(CNDX_MAX_STACK * ctSIZE(PLEAF));

if (!ctcidxStk) {

printf("Unable to allocate memory for run-time stack.\n");

ctrt_exit(1);

}

}


/* Set up a buffer containing the field data used in the expression. */

Get_Buffer(&recbuf);


/* Evaluate the expression. */

retcidx = cndxeval(ptree, recbuf, (pConvMap)schema);

if (retcidx<0)

printf("The expression cannot be evaluated for this record

- error %d.\n", uerr_cod);

else if (retcidx)

printf("The expression evaluates to TRUE for this record.\n");

else

printf("The expression evaluates to FALSE for this record.\n");

Remember to always free any memory used by your record schema, field name list, and expression tree when you are finished with them. Use the following FairCom DB functions to do so:

  • mbfree(schema)
  • mbfree(names)
  • cndxfree(ptree)

Previous Topic

Next Topic

Variable-length Records with Conditional Expressions

When using data filters or conditional indexes with variable length data files, a record retrieval that does not bring back the entire record will return a CVAL_ERR error (598), which indicates the expression could not be evaluated.

There are two types of retrievals that result in less than the entire record being read:

  1. Calling the fixed-length versions of the ISAM routines such as FirstRecord() or NextRecord() instead of FirstVRecord() or NextVRecord(). The fixed-length calls cannot be used to read variable-length records with a data filter or conditional index.
  2. Calling the variable-length versions of the ISAM routines with a buffer length insufficient to hold the entire record.

When an ISAM call fails (with CVAL_ERR (598) or some other error), the current ISAM position is NOT updated. Therefore the following pseudo-code sequence will NOT work because the FirstRecord() did not establish the failing record as the current ISAM position, and the GETVLEN() call would reference the record at the current ISAM position before the FirstRecord() call:

SETFLTR(datno,...);

if (FRSREC(...) == CVAL_ERR) {

vlen= GETVLEN(datno);

rc= FRSVREC(...,&vlen);

}

Using the variable-length versions of the ISAM routines provides a workable approach. The following pseudo-code works, with one proviso - the subsequent calls to the ISAM routine can also fail with a CVAL_ERR (598) because they may have skipped forward to an even larger record:

SETFLTR(datno,...);

oldlen = vlen;

if (FRSVREC(...,bufr,&vlen) == CVAL_ERR && oldlen < vlen) {

free(bufr);

oldlen = vlen;

bufr = calloc(vlen);

rc = FRSVREC(...,bufr,&vlen);}

The second call to FirstVRecord() could also return the CVAL_ERR (598) because while the record that originally caused the CVAL_ERR (598) can now be read completely, if it failed the filter, the next records will be read automatically until a record is found that passes the filter; but these subsequent reads can also encounter a record that is bigger than the new buffer size.

The following pseudo-code loop should work with any of the variable length versions of the ISAM calls:

SETFLTR(datno,...);

oldlen= vlen;

while CVAL_ERR == (xyzVREC(...,bufr,&vlen) && oldlen < vlen) {

free(bufr);

oldlen = vlen;

bufr = calloc(vlen);

}

if (isam_err)

then problem or no record found

else

success

If one knows ahead of time that there is a maximum record length for the file, then simply using a buffer of this known maximum size eliminates the need to loop over the CVAL_ERR (598) caused by an insufficient buffer size.

TOCIndex