Product Documentation

Conditional Expressions

Previous Topic

Next Topic

FairCom DB Expression Parser and Grammar

A powerful expression parser/analyzer provides for complex conditional expressions that can be defined and evaluated at runtime.

Filter expression syntax closely follows the C language syntax for expressions, including order of precedence. An expression interpreted by the expression parser should compile without errors with a standard C compiler. As in C, you cannot compare strings directly like LastName > 'S'. However, the expression parser has a number of built-in functions that allow the comparison of strings. Example:

strcmp( LastName, "S" ) > 0

The expression handling assures proper alignment considerations are handled, and ensures buffer size of any record being evaluated is big enough.

Routines that evaluate conditional expressions maintain fixed data record lengths and total data record lengths. This permits correct alignment adjustments and detects if insufficient data is available. The latter condition results in a CVAL_ERR (598) error. The easiest way to produce a CVAL_ERR (598) is to read only the fixed-length portion of a data record, and have an expression that relies on fields in the variable-length portion of the record.

For additional control, a Conditional Expression Callback Function is available. This allows advanced control through an external user created function.

See also

In This Section

Constants

Variables

Parentheses

Type Casting

Automatic Type Promotion

Operators

Variable-length Records with Conditional Expressions

Previous Topic

Next Topic

Constants

The expression parser uses the constants below internally as a 32-bit signed or unsigned integer:

  • Character constants: any valid char enclosed in single quotes, e.g., 'a'
  • Signed Integer constants: values from - 2,147,438,647 to 2,147,438,647
  • Unsigned Integer constants: values from 0 to 4,294,967,295
  • Hexadecimal constants: values from 0x00000000 to 0xffffffff. Any combination of lower case or upper case letters are accepted, e.g., 0Xbc4f or 0xF5C56d
  • Date constants:

{d'yyyy-[m]n-[d]d'}

yyyy is 4 digit year

[m]m is 1 or 2 digit month

[d]d is 1 or 2 digit day

Examples:

{d'2024-12-06'}

{d'2024-8-6'}

 

'[m]m-[d]d-yyyy' or '[m]m/[d]d/yyyy'

yyyy is 4 digit year

[m]m is 1 or 2 digit month

[d]d is 1 or 2 digit day

Examples:

'12-06-2024'

'8-6-2024'

'8/6/2024'

 

'yyyy-[m]m-[d]d' or 'yyyy/[m]m/[d]d'

yyyy is 4 digit year

[m]m is 1 or 2 digit month

[d]d is 1 or 2 digit day

Examples:

'2024-12-06'

'2024-8-6'

'2024/8/6'

 

'[d]d-mmm-yyyy' or '[d]d/mmm/yyyy'

[d]d is 1 or 2 digit day

mmm is 3 letter month name

yyyy is 4 digit year

month names (case insensitive) are:

JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC

Examples:

'1/jan/2024'

'12-feb-2024'

 

  • Time constants:

{t'[h]h:[m]m:[s]s'}

[h]h is 1 or 2 digit hour

[m]m is 1 or 2 digit minute

[s]s is 1 or 2 digit second

Examples:

{t'23:11:33'}

{t'1:2:3'}

 

'[h]h:[m]m:[s]s[:[i][i]i]'

[h]h is 1 or 2 digit hour

[m]m is 1 or 2 digit minute

[s]s is 1 or 2 digit second

[i][i]i is optional 1 to 3 digit millisecond

Examples:

'1:2:3'

'12:33:44.123'

 

  • Timestamp constants:

{ts'yyyy-[m]m-[d]d [h]h:M[M]:s[s]'}

yyyy is 4 digit year

[m]m is 1 or 2 digit month

[d]d is 1 or 2 digit day

[h]h is 1 or 2 digit hour

[M]M is 1 or 2 digit minute

[s]s is 1 or 2 digit second

Examples:

{ts'2024-08-12 22:33:44'}

{ts'2024-08-12 2:3:4'}

 

'[m]m-[d]d-yyyy [h]h:[M]M:[s]s[:[i][i]i]' or '[m]m/[d]d/yyyy [h]h:[M]M:[s]s[:[i][i]i]'

[m]m is 1 or 2 digit month

[d]d is 1 or 2 digit day

yyyy is 4 digit year

[h]h is 1 or 2 digit hour

[M]M is 1 or 2 digit minute

[s]s is 1 or 2 digit second

[i][i]i is optional 1 to 3 digit millisecond

Examples:

'08-12-2024 22:33:44'

'08/12/2024 2:3:4.123'

 

'yyyy-[m]m-[d]d [h]h:[M]M:[s]s[:[i][i]i]' or 'yyyy/[m]m/[d]d [h]h:[M]M:[s]s[:[i][i]i]'

yyyy is 4 digit year

[m]m is 1 or 2 digit month

[d]d is 1 or 2 digit day

[h]h is 1 or 2 digit hour

[M]M is 1 or 2 digit minute

[s]s is 1 or 2 digit second

[i][i]i is optional 1 to 3 digit millisecond

Examples:

'2024-08-12 22:33:44'

'2024/08/12 2:3:4.123'

 

'[d]d-mmm-yyyy [h]h:[M]M:[s]s[:[i][i]i]' or '[d]d/mmm/yyyy [h]h:[M]M:[s]s[:[i][i]i]'

[d]d is 1 or 2 digit day

mmm is 3 letter month name

yyyy is 4 digit year

[h]h is 1 or 2 digit hour

[M]M is 1 or 2 digit minute

[s]s is 1 or 2 digit second

[i][i]i is optional 1 to 3 digit millisecond

month names (case insensitive) are:

JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC

Examples:

'08-dec-2024 22:33:44'

'08/dec/2024 2:3:4.123'

 

Any integer larger than the maximum size allowed for integers, or any number with decimal points, or any numbers in scientific notation are interpreted as a floating point constant by the expression parser .

String constants are similar to C string constants and represent any text enclosed by double quotes, for example, "This is a string". The maximum size of a string constant defaults to 255 characters. The filter expression parser allows the following escape characters in string constants or character constants:

Escape char

Value

Explanation

\a or \A

ASCII 7

bell

\b or \B

ASCII 8

backspace

\f or \F

ASCII 12

Form Feed

\n or \N

ASCII 10

Linefeed

\r or \R

ASCII 13

Carriage Return

\t or \T

ASCII 9

tab

\v or \V

ASCII 11

vertical tab

\\

 

 

\any

 

Any character not listed above

Previous Topic

Next Topic

Variables

A filter expression variable is actually the name of the fields defined for the table. There is a limit of 128 characters for the name of variables and the names are case sensitive.

When a user specifies a variable name, the filter parser searches the table definition for a field of that name. The parser uses the type of the field and converts it to the types used internally by the expression evaluator. The conversion of field types is as follows:

Field Type

Data Type

Field Type

Data Type

CT_BOOL

int

CT_SFLOAT

double

CT_CHAR

int

CT_DFLOAT

double

CT_CHARU

unsigned

CT_FSTRING

char*

CT_INT2

int

CT_FPSTRING

char*

CT_INT2U

unsigned

CT_F2STRING

char*

CT_INT4

int

CT_F4STRING

char*

CT_INT4U

unsigned

CT_STRING

char*

CT_DATE

unsigned

CT_PSTRING

char*

CT_TIME

unsigned

CT_2STRING

char*

CT_MONEY

int

CT_4STRING

char*

Please note that "int" is a LONG, "unsigned" is a ULONG and "char*" is a pTEXT.

Field names that match a valid expression reserved word:

Consider a field named "year", which collides with the function YEAR. The expression "[year] == 2000" is needed to handle "year" as a field name rather than the function.

Previous Topic

Next Topic

Parentheses

Use parentheses exactly like they are used in C expressions. There are no limits on the number of parentheses you may use in an expression, as long as each open parenthesis has a closing parenthesis. Parentheses are also used to enclose the arguments of built-in functions.

Previous Topic

Next Topic

Type Casting

The filter expression parser allows you to use explicit type casts in expressions. This is very useful if you are comparing fields of different types and want to control the result of an expression.

For example, suppose "Salary" is a CT_MONEY field and "Average" is a CT_DFLOAT field; type casts can be used as illustrated in the following expression: (Salary - (int)Average) > 500

The following type casts may be used in conditional expressions:

  • (int) or (long): Convert the result of expression to integer (32 bit).
  • (unsigned [int | long]): Convert the result of expression to unsigned integer (32 bit).
  • (double): Convert the result of expression to double.

You cannot type cast a string expression.

Previous Topic

Next Topic

Automatic Type Promotion

When mixing different types in an expression without explicit type casting, the conditional expression parser automatically promotes the types using the following rule:

  1. signed and unsigned integers - promoted to unsigned integer (64-bit)
  2. signed integer and double - promoted to double
  3. unsigned integer and double - promoted to double

In the great majority of cases, mixing strings with numeric values returns a parsing error.

Previous Topic

Next Topic

Operators

The following operators are allowed in filters and conditional expressions.

Mathematical Operators

+

Adds two operands

-

Subtracts two operands or negates an operand (e.g., -5)

*

Multiplication

/

Division

%

Modulus

Relational Operators

==

Equal to

!=

Not equal to

<

Less than

<=

Less or equal to

>

Greater than

>=

Greater than or equal to

Logical Operators

&&

And

||

Or

!

Not

Binary Operators

&

And

|

Or

~

Not

^

Xor

NULL Operators

IS NULL

IS NOT NULL

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