![]() |
![]() |
|
|
Overview
Introduction
This chapter begins by describing two ways in which the idea of fielded records or fielded buffers can be handled: through structured records and through FML32 records. It then describes the features of the Field Manipulation Language and the circumstances under which you might want to use them.
A comparison of FML32 records with traditional structured records clearly shows the advantages of using fielded buffers throughout an application.
Dividing Records into Fields
Unless a data record is a complete and indivisible entity (an unusual situation), you need to be able to break a record into fields so you can use or change the information in the record. In BEA MessageQ applications there are two ways to divide records into fields:
One common way of subdividing records is with a structure that divides a contiguous area of storage into fields. The fields are given names for identification; the kind of data carried in the field is shown by the data type declaration.
For example, a data item in a C language program that contains information about an employee's identification number, name, address, and sex, may be formatted in a structure such as the following:
struct S {
long empid;
char name[20];
char addr[40];
char sex;
};
where the data type of the empid field is declared to be a long integer, name and addr are declared to be character arrays of 20 and 40 characters respectively, and sex is declared to be a single character (presumably with a range of m or f).
If, in your C program, the variable p points to a structure of type struct S, the references p->empid, p->name, p->addr and p->sex can be used to address the fields.
Possible Disadvantages of Structures
While this way of representing data is widely used and often appropriate, it has two major potential disadvantages:
Fielded buffers provide another way of subdividing a record into fields.
A fielded buffer is a data structure that provides associative access to the fields of a record; that is, the name of a field is associated with an identifier that includes the storage location as well as the data type of the field.
The main advantage of the fielded buffer is data independence. Fields can be added to the buffer, deleted from it, or changed in length without forcing programs that reference the fields to be recompiled. To achieve this data independence, a field is referenced by an identifier rather than by the fixed offset prescribed by record structures, and all access to fields is through function calls.
Fielded buffers can be used throughout a BEA MessageQ application as the standard method of representing data sent between cooperating processes.
Implementing Fielded Buffers with FML32
Fielded buffers are created, updated, accessed, input, and output via the Field Manipulation Language (FML). FML32 has two main objectives:
FML32 is implemented as a library of functions and macros that can be called from C programs. There are two major groups of FML32 functions:
This section describes the features of FML32 and recommends how to use them in application programs.
Fielded Buffer Structure
A fielded buffer, as mentioned earlier, is a data structure that provides associative access to the fields of a record.
Each field in an FML32 fielded buffer is labeled with an integer that combines information about the data type of the accompanying field with a unique identifying number. The label is called the field identifier, or fldid32. For variable-length items, fldid32 is followed by a length indicator. The buffer can be represented as a sequence of fldid/data pairs, with fldid/length/data triples for variable-length items. Figure 2-1 illustrates this.
Figure 2-1 A fielded buffer
In the header file that is #include'd whenever FML32 functions are used (fml32.h), field identifiers are typedef'd as FLDID32, field value lengths as FLDLEN32, and field occurrence numbers as FLDOCC32.
Supported Field Types
The supported field types are short, long, float, double, character, string, and carray (character array). These types are #define'd in fml32.h as shown in Listing 2-1.
Listing 2-1 FML32 field types as defined in fml32.h
#define FLD_SHORT 0 /* short int */
#define FLD_LONG 1 /* long int */
#define FLD_CHAR 2 /* character */
#define FLD_FLOAT 3 /* single-precision float */
#define FLD_DOUBLE 4 /* double-precision float */
#define FLD_STRING 5 /* string - null terminated */
#define FLD_CARRAY 6 /* character array */
FLD_STRING and FLD_CARRAY are both arrays, but differ in the following ways:
Functions that add or change a field have a FLDLEN argument that must be filled in when you are dealing with FLD_CARRAY fields. The size of a string or carray is limited to 2 billion bytes for FML32.
It is not a good idea to store unsigned data types in fielded buffers. You should either convert all unsigned short data to long or cast the data into the proper unsigned data type whenever you retrieve data from fielded buffers (using the FML32 conversion functions).
Most FML32 functions do not perform type checking; they expect that the value you update or retrieve from a fielded buffer matches its native type. For example, if a buffer field is defined to be a FLD_LONG, you should always pass the address of a long value. The FML32 conversion functions convert data from a user specified type to the native field type (and from the field type to a user specified type) in addition to placing the data in (or retrieving the data from) the fielded buffer.
Field Name to Identifier Mappings
A field is usually referred to by its field identifier (fldid32), an integer. (See Field Definition and Use, for a detailed description of field identifiers). This allows you to reference fields in a program without using the field name, which may change.
There are two ways in which identifiers are assigned (mapped) to field names:
A typical application might use one or both of the above methods to map field identifiers to field names.
In order for FML32 to access the data in fielded records, there must be some way for FML32 to access the field name/identifier mappings. FML32 gets this information in one of two ways:
Field name/identifier mappings can be made available to FML32 programs at run-time through field table files. It is the responsibility of the programmer to set two environment variables that tell FML32 where the field name/identifier mapping table files are located.
The environment variable FLDTBLDIR32 contains a list of directories where field tables can be found. The environment variable FIELDTBLS32 contains a list of the files in the table directories that are to be used.
Within application programs, the FML32 function Fldid32() provides for a run-time translation of a field name to its field identifier. Fname32() translates a field identifier to its field name (see Fldid(3fml) and Fname(3fml)). The first invocation of either function causes space in memory to be dynamically allocated for the field tables and the tables to be loaded into the address space of the process. The space can be recovered when the tables are no longer needed. (See "Loading the Field Tables" in Chapter 4.)
This method should be used when field name/identifier mappings are likely to change throughout the life of the application. This topic is covered in more detail in Chapter 4.
Compile-Time: Header Files
mkfldhdr32(1) is provided to make header files out of field table files. These header files are #include'd in C programs, and provide another way to map field names to field identifiers: at compile-time.
Using field header files, the C preprocessor converts all field name references to field identifiers at compile-time; thus, you do not need to use the Fldid32() or Fname32() functions as you would with the field table files described in the previous section.
If you always know the field names needed by your program, you can #include your field table header file(s), saving some data space and enabling your program to run more quickly.
However, since this method resolves mappings at compile-time, it should not be used if the field name/identifier mappings in the application are likely to change. This topic is covered in more detail in Chapter 4.
Fielded Buffer Indexes
When a fielded buffer has many fields, access is expedited in FML32 by the use of an internal index. The user is normally unaware of the existence of this index.
Fielded buffer indexes do, however, take up space in memory and on disk. When you store a fielded buffer on disk, or transmit a fielded buffer between processes or between computers, you can save disk space and/or transmittal time by first discarding the index.
FML32 provides the Funidex32() function for discarding the index. When the fielded buffer is read from disk (or received from a sending process), the index can be explicitly reconstructed with the function Findex32().
Note that these space savings do not apply to memory. The function Funidex32() does not recover in-core memory used by the index of a fielded buffer.
Multiple Occurrences of Fields
A fielded buffer may contain more than one occurrence of any field. Many FML32 functions take an argument that specifies which occurrence of a field is to be retrieved or modified. If a field occurs more than once, the first occurrence is numbered 0, and additional occurrences are numbered sequentially. The set of all occurrences constitutes a logical sequence, but no overhead is associated with the occurrence number (that is, it is not stored in the fielded buffer).
If another occurrence of a field is added, it is added at the end of the set and is referred to as the next highest occurrence. When an occurrence other than the highest is deleted, all higher occurrences of the field are shifted down by one (for example, occurrence 6 becomes occurrence 5, 5 becomes 4, and so on).
Boolean Expressions and Fielded Buffers
Often, application programs receive a fielded buffer from another source (from a user's terminal, from a database record, and so on) and the values of one or more fields determine the next action taken by the application program. FML32 provides several functions that create boolean expressions on fielded buffers and determine if a given buffer meets the criteria specified by the expression.
Once you create a boolean expression, it is compiled into an evaluation tree. The evaluation tree is then used to determine if a fielded buffer matches the specified boolean conditions.
For instance, a program may read a data record into a fielded buffer (Buffer A) and apply a boolean expression to the buffer. If Buffer A meets the conditions specified by the boolean expression, then an FML32 function is used to update another buffer, Buffer B, with data from Buffer A.
Error Handling
When an FML32 function detects an error, one of the following values is returned:
All FML32 function call returns should be checked against the appropriate value above to detect errors.
In all error cases, the external integer Ferror32 is set to the error number as defined in fml32.h.
The F_error32 function is provided to produce a message on the standard error output. It takes one parameter, a string; prints the argument string appended with a colon and a blank; and then prints an error message followed by a newline character. The error message displayed is the one defined for the error number currently in Ferror32, which is set when errors occur.
To be most useful, the argument string to the F_error32() function should include the name of the program that incurred the error.
Fstrerror32() can be used to retrieve (from a message catalog) the text of an error message; it returns a pointer that can be used as an argument to F_error32().
The error codes that can be produced by an FML32 function are described on the page that documents the function in the BEA MessageQ Reference Manual.
|
Copyright © 2000 BEA Systems, Inc. All rights reserved.
|