Variable and Type Declarations
The following section describes variable and type declarations.
EQUEL Variable Declaration Procedures
You must make known to the processor any C language variable that you use in an EQUEL statement so that it can determine the type of the variable. You must precede the variable declaration in an EQUEL/C program by two number signs (##) that begin in the first column position of the line. If a variable is not used in an EQUEL statement, you do not need to use number signs.
Reserved Words in Declarations
In declarations, all EQUEL keywords are reserved. Therefore, you cannot declare types or variables with the same name as those keywords. Also, the following EQUEL/C keywords, used in declarations, are reserved and cannot be used elsewhere, except in quoted string constants:
Note: Not all C compilers reserve every keyword listed. However, the EQUEL/C preprocessor does reserve all these words.
The EQUEL preprocessor does not distinguish between uppercase and lowercase in keywords. When generating C code, it converts any uppercase letters in keywords to lowercase. The following example shows that although the following declarations are initially unacceptable to the C compiler, the preprocessor converts them into legitimate C code. Lines without ## in the first two column positions pass through without case conversion.
## defINE ARRSIZE 256
## /* "defINE" is converted to "define" */
## INT numarr[ARRSIZE];
## /* "INT" is equivalent to "int" */
The rule just described is true only for keywords. The preprocessor does distinguish between case in program-defined types and variable names.
Variable and type names must be legal C identifiers beginning with an underscore or alphabetic character.
Data Types
The EQUEL/C preprocessor accepts the C data types in the following table and maps them to corresponding Ingres types. For further information on type mapping between Ingres and C data, see
Data Type Conversion.
C Data Types and Corresponding Ingres Types
The Integer Data Type
The EQUEL preprocessor accepts all C integer data types. Even though some integer types do have C restrictions (for example, values of type
short must be in the limits of your machine). The preprocessor does not check these restrictions. At runtime, data type conversion is determined according to standard C numeric conversion rules. For details on numeric type conversion, see
Data Type Conversion.
The type adjectives long, short, or unsigned can qualify the integer type.
In the type mappings table just presented, the C data type char has three possible interpretations, one of which is the Ingres integer data type. The adjective unsigned can qualify the char type when it is used as a single-byte integer. If a variable of the char data type is declared without any C indirection, such as an array subscript or a pointer operator (the asterisk), it is considered a single-byte integer variable. For example:
## char age;
is a legal declaration and can be used to hold an integer Ingres value. If the variable is declared with indirection, it is considered an Ingres character string.
You can use an integer variable with any numeric-valued object to assign or receive numeric data. For example, you can use it to set a field in a form or to select a column from a database table. It can also specify simple numeric objects, such as table field row numbers.
The following example shows the way several C data types are interpreted by EQUEL:
## char age; /* Single-byte integer */
## short empnums[MAXNUMS]; /* Short integers array*/
## long *global_index; /* Pointer to long integer */
## unsigned int overtime;
The Floating-point Data Type
The preprocessor accepts float and double as floating-point data types. The internal format of double variables is the standard C runtime format.
You can only use a floating-point variable to assign or receive numeric data. You cannot use it to specify numeric objects, such as table field row numbers. Note that long float, a construct allowed in some compilers, is accepted as a synonym for double.
## float salary;
## double sales;
VMS: If you declare long floating variables to be used with EQUEL statements, you should not compile your program with the
g_float command line qualifier when you are using the VAX C compiler. This qualifier changes the long float internal storage format, causing runtime numeric errors.
The Character String Data Type
Any variables built up from the
char data type, except simple variables declared without any C indirection, are compatible with any Ingres character string objects. As previously mentioned, a variable of type
char declared without any C indirection is considered an integer variable. The preprocessor treats an array of characters and a pointer to a character string in the same way. Always null-terminate a character string if the string is to be assigned to an Ingres object. automatically null terminates any character string values that are retrieved into C character string variables. Consequently, any variable that you use to receive Ingres values must be declared as the maximum object length, plus one extra byte for the C null string terminator. For more information, see
Runtime Character Conversion.
The following example declares three character variables—one integer and two strings:
## char age; /* Single byte integer */
## char *name; /* To be a pointer to a string */
## charbuf[16];
/*
** To be used to receive at most 15 bytes of string
** data, plus a null string terminator
*/
For more information on character strings that contain embedded nulls, see
The Varying Length String Type.
## Define Declaration
The EQUEL preprocessor accepts the ## define directive, which defines a name to be a constant value. The EQUEL preprocessor replaces the ## define statement with the C # define statement.
The syntax for the ## define statement is:
## define constant_name constant_value
Syntax Notes:
• The constant_value must be an integer, floating-point, or character string literal. It cannot be an expression or another name. It cannot be left blank, as would happen if you intend to use it later with the # ifdef statement. If the value is a character string constant, you must use double quotes to delimit it. Do not use single quotes to delimit constant_name in order to make it be interpreted as a single character constant, because the preprocessor translates the single quotes into double quotes. For example, both of the following names are interpreted as string constants, even though the first may be intended as a character constant:
## define QUITFLAG 'Q'
## define ERRORMSG "Fatal error occurred."
• The preprocessor does not accept casts before constant_value. In general, the preprocessor does not accept casts, and it interprets data types from the literal value.
You can only use a defined constant to assign values to Ingres objects. Attempting to retrieve Ingres values into a constant causes a preprocessor error.
## define minempnum 1
## define maxsalary 150000.00
## define defaultnm "No-name"
EQUEL does not recognize a C define declaration with only one #.
Variable Declarations Syntax
The syntax of a variable declaration is:
[storage_class] type_specification
declarator {, declarator};
where each declarator is:
variable_name [= initial_value]
Syntax Notes:
• Storage_class is optional, but if specified can be any of the following:
auto
extern
register
static
varchar
VMS also uses globaldef and globalref unless you are using ANSI C on VMS.
The storage class provides no data type information to the preprocessor. For more detail on the EQUEL-defined
varchar storage class, see
The Varying Length String Type.
• Although register variables are supported, be careful when using them in EQUEL statements. In database statements, such as the append and retrieve statements, the preprocessor generates C function calls which may pass a variable by reference using the ampersand operator (&). However, some compilers do not allow you to use register variables in this manner.
• Because of the syntactic similarity between the EQUEL register statement and the C register declaration, the preprocessor does not allow you to represent the initial object name in the EQUEL register statement with a host variable.
• The
type_specification must be an EQUEL type, a type built up with a
typedef declaration (and known to the preprocessor), or a structure or union specification.
For a discussion of
Typedef declarations, see
Type Declarations Syntax. For a discussion of structures, see
Structure Declarations Syntax.
• Precede the
variable_name by an asterisk (*), to denote a pointer variable, or follow it by a bracketed expression (
[expr]), to denote an array variable. For a discussion of pointers, see
Pointer Declarations Syntax. For a discussion of arrays, see
Array Declarations Syntax.
• Begin the variable_name with a legal C identifier name that starts with an underscore or alphabetic character.
• The preprocessor does not evaluate the initial_value. Consequently, the preprocessor accepts any initial value, even if it can later cause a C compiler error. For example, the preprocessor accepts both of the following initializations, even though only the first is a legal C statement:
## char *msg = "Try again";
## int rowcount = {0, 123};
The following example illustrates some valid EQUEL/C declarations:
## extern int first_employee;
## auto long update_mode = 1;
## static char *names[3] = {"neil", "mark", "barbara"};
## static char *names[3] = {"john", "bob", "tom"};
## char **nameptr = names;
## short name_counter;
## float last_salary = 0.0, cur_salary = 0.0;
## double stat_matrix[STAT_ROWS][STAT_COLS];
Type Declarations Syntax
The syntax of a type declaration is:
typedef type_specification
typedef_name {, typedef_name};
Syntax Notes:
• The typedef keyword acts like a storage class specifier in a variable declaration, except that the resulting typedef_name is marked as a type name and not as a variable name.
• The
type_specification must be an EQUEL/ C type, a type built up with a
typedef declaration and known to the preprocessor, or a structure or union specification. For a discussion of structures, see
Structure Declarations Syntax.
• Precede the
typedef_name by an asterisk (*)
, to denote a
pointer type, or follow it by a bracketed expression ([
expr]), to denote an array type. For a discussion of pointers, see
Pointer Declarations Syntax. For a discussion of arrays, see
Array Declarations Syntax.
• The preprocessor accepts an initial value after typedef_name, although you should avoid putting one there because it would not signify anything. Most C compilers allow an initial value that is ignored after the typedef_name. The initial value is not assigned to any variables declared with that typedef.
## typedef short INTEGER2;
## typedef char CHAR_BUF[2001], *CHAR_PTR;
## INTEGER2 i2;
## CHAR_BUF logbuf;
## CHAR_PTR name_ptr = (char *)0;
Array Declarations Syntax
The syntax of a C array declaration is:
array_name[dimension] {[dimension]}
In the context of a simple variable declaration, the syntax is:
type_specification array_variable_name[dimension] {[dimension]};
In the context of a type declaration, the syntax is:
typedef type_specification array_type_name[dimension]
{[dimension]};
Syntax Notes:
• The preprocessor does not evaluate the dimension specified in the brackets. Consequently, the preprocessor accepts any dimensions, including illegal dimensions such as non-numeric expressions, which later cause C compiler errors.
For example, the preprocessor accepts both of the following declarations, even though only the second is legal C:
## typedef int SQUARE["bad expression"];
/* Non-constant expression */
## int cube_5[5][5][5];
• You can specify any number of dimensions. The preprocessor notes the number of dimensions when the variable or type is declared. When the variable is later referenced, it must have the correct number of indices.
• An array variable can be initialized, but the preprocessor does not verify that the initial value is an array aggregate.
• An array of characters is considered to be the pseudo character string type.
The following example illustrates the use of array declarations:
## define COLS 5
## typedef short SQUARE[COLS][COLS];
## SQUARE sq;
## static int matrix[3][3] =
## { {11, 12, 13},
## {21, 22, 23},
## {31, 32, 33} };
## char buf[50];
Pointer Declarations Syntax
The syntax of a C pointer declaration is:
*{*} pointer_name
In the context of a simple variable declaration, the syntax is:
type_specification *{*} pointer_variable_name;
In the context of a type declaration, the syntax is:
typedef type_specification *{*} pointer_type_name;
Syntax Notes:
• You can specify any number of asterisks. The preprocessor notes the number specified when the variable or type is declared. When the variable is later referenced, it must have the correct number of asterisks.
• A pointer variable can be initialized, but the preprocessor does not verify that the initial value is an address.
• A pointer to the char data type is considered to be the pseudo character string type.
• You can use arrays of pointers.
The following example illustrates the use of pointer declarations:
## extern int min_value;
## int *valptr = &min_value;
## char *tablename = "employee";
Structure Declarations Syntax
A C structure declaration has three variants depending on whether it has a tag and/or a body. The following sections describe these variants.
A Structure with a Tag and a Body
The syntax of a C structure declaration with a tag and a body is:
struct tag_name {
structure_declaration {structure_declaration}
};
where structure_declaration is:
type_specification
member {, member};
In the context of a simple variable declaration, the syntax is:
struct tag_name {
structure_declaration {structure_declaration}
} [structure_variable_name];
In the context of a type declaration, the syntax is:
typedef struct tag_name {
structure_declaration {structure_declaration}
} structure_type_name;
Syntax Notes:
• Wherever the keyword struct appears, the keyword union can appear instead. The preprocessor treats them as equivalent.
• Each member in a structure_declaration has the same rules as a variable of its type. For example, as with variable declarations, the type_specification of each member must be a previously defined type or another structure. Also, you can precede the member name by asterisks or follow it by brackets. Because of the similarity between structure members and variables, the following discussion focuses only on those areas in which they differ.
• A structure member can be a nested structure declaration. For example:
## struct person
## {
## charname[40];
## struct
## {
## int day, month, year;
## } birth_date;
## } owner;
• Only structure members that will be referenced in EQUEL statements need to be declared to EQUEL. The following example declares a C structure with the fileloc member that is not known to EQUEL:
## struct address {
## int number;
## char street[30];
## char town[20];
## short zip;
FILE *fileloc; /* Unknown to EQUEL */
## } addr[20];
• Although the preprocessor permits an initial value after each member name, do not put one there because it causes a compiler syntax error.
• If you do not specify the structure_variable_name, the declaration is considered a declaration of a structure tag.
• A structure variable can be initialized, but the preprocessor does not verify that the initial value is a structure aggregate.
The following example illustrates the use of a tag and a body:
## define max_employees 1500
## typedef struct employee
## {
## char name[21];
## short age;
## double salary;
## } employee_desc;
## employee_desc employees[MAX_EMPLOYEES];
## employee_desc *empdex = &employees[0];
A Structure with a Body and No Tag
The syntax of a C structure declaration with a body and no tag is:
struct {
structure_declaration {structure_declaration}
} structure_variable_name;
where structure_declaration is the same as in the previous section.
In the context of a simple variable declaration, the structure's syntax is:
struct {
structure_declaration {structure_declaration}
} structure_variable_name;
In the context of a type declaration, the structure's syntax is:
typedef struct {
structure_declaration {structure_declaration}
} structure_type_name;
Syntax Notes:
• All common clauses have the same rules as in the previous section. For example, struct and union are treated as equivalent, and the same rules apply to each structure member as to variables of the same type.
• Specify the structure_variable_name when there is no tag. In fact, the actual structure definition applies only to the variable being declared.
The following example illustrates the use of a body with no tag:
## define MAX_EMPLOYEES 1500
## struct
## {
## char name[21];
## short age;
## double salary;
## } employees[MAX_EMPLOYEES];
A Structure with a Tag and No Body
The syntax of a C structure declaration with a tag and no body is:
struct tag_name
In the context of a simple variable declaration, the syntax is:
struct tag_name structure_variable_name;
In the context of a type declaration, the syntax is:
typedef struct tag_name structure_type_name;
Syntax Notes:
• All common clauses have the same rules as in the previous section. For example, struct and union are treated as equivalent, and the structure can be initialized without the preprocessor checking for a structure aggregate.
• The tag_name must refer to a previously defined structure or union. The preprocessor does not support forward structure declarations. Therefore, when referencing a structure tag in this type of declaration, the tag must have already been defined. In the declaration below, the tag "new_struct" must have been previously declared:
## typedef struct new_struct *NEW_TYPE;
The following example illustrates the use of a tag and no body:
## union a_name
## {
## char nm_full[30];
## struct
## {
## char nm_first[10];
## char nm_mid[2];
## char nm_last[18];
## } nm_parts;
## };
## union a_name empnames[MAX_EMPLOYEES];
Enumerated Integer Types
An enumerated type declaration, enum, is treated as an integer declaration. The syntax of an enumerated type declaration is:
enum [enum_tag]
{ enumerator [= integer_literal]
{, enumerator [= integer_literal]} } [enum_vars];
The outermost braces ( { and } ) represent actual braces you type.
Syntax Notes:
• If you use the enum_tag, the list of enumerated literals (enumerators) and enum variables (enum_vars) is optional, as in a structure declaration. The two declarations that follow are equivalent. The first declaration declares an enum_tag, while the second declaration uses that tag to declare a variable.
First declaration:
## enum color {RED, WHITE, BLUE};
/* Tag, no variable */
## enum color col; /* Tag, no body, has variable */
Second declaration:
## enum color {RED, WHITE, BLUE} col;
/* Tag, body, has variable */
If you do not use the enum_tag, the declaration must include a list of enumerators, as in a structure declaration.
• You can use the enum declaration with any other variable declaration, type declaration, or storage class. For example, the following declarations are all legal:
## typedef enum {dbTABLE, dbCOLUMN, dbROW,
## dbVIEW, dbGRANT} dbOBJ;
## dbOBJ obj, objs[10];
## extern dbOBJ *obj_ptr;
• Enumerated variables are treated as integer variables and enumerated literals are treated as integer constants.
The Varying Length String Type
All C character strings are
null-terminated. (For more information, see
The Character String Data Type (see
The Character String Data Type). Ingres data of type
char or
varchar can contain random binary data including the zero-valued byte (the null byte or "\0" in C terms). If a program uses a C
char variable to retrieve or set binary data that includes nulls, the EQUEL runtime system is not able to differentiate between embedded nulls and the null terminator. Unlike other programming languages, C does
not blank-pad fixed length character strings.
In order to set and retrieve binary data that may include nulls, a new EQUEL/C storage class, varchar, has been provided for varying length string variables. varchar identifies the following variable declaration as a structure that describes a varying length string, namely, a 2-byte integer representing the count and a fixed length character array. Like other storage classes previously described, the keyword varchar must appear before the variable declaration:
## varchar struct {
## short current_length;
## char data_buffer[MAX_LENGTH];
## } varchar_structure;
Syntax Notes:
• The word varchar is reserved and can be in uppercase or lowercase.
• The varchar keyword is not generated to the output C file.
• The varchar storage class can only refer to a variable declaration, not to a type definition. For example, the following declaration is legal because it declares the variable "vch":
## varchar struct {
## short buf_size;
## char buf[100];
## } vch;
But the varchar declaration of the structure tag "vch" (without a variable) is not legal and will generate an error:
## varchar struct vch {
## short buf_size;
## char buf[100];
## };
• The structure definition of a varchar variable declaration can be replaced by a structure tag or typedef reference. For example the following typedef and varchar declarations are legal:
## typedef struct vch_ {
## short vch_count;
## char vch_data[VCH_MAX];
## } VCH;
## varchar VCH vch_1; /* typedef referenced */
## varchar struct vch_ vch_2;
## /* structure tag referenced */
• The varchar storage class can be used for any type of variable declaration, including external and static variables, and to qualify nested structure members. For example, the following declarations are all legal:
## static varchar struct _txt {
## /* with storage class "static"*/
## short tx_len;
## char tx_data[TX_MAX];
## } txt_var, *txt_ptr, txt_arr[10];
## struct {
## char ename[20];
## int eage;
## varchar struct_txt ecomments;
## /* nested in structure */
## } emp;
## typedef short BUF_SIZE;
## typedef char BUF[512];
## varchar struct {/* members are typedef'd */
## BUF_SIZE len;
## BUF data;
## } vchar;
Indicator Variables
An indicator variable is a 2-byte integer variable. You can use these in three ways in an application:
• In a statement that retrieves data from Ingres, you can use an indicator variable to determine if its associated host variable was assigned a null.
• In a statement that sets data to Ingres, you can use an indicator variable to assign a null to the database column, form field, or table field column.
• In a statement that retrieves character data from Ingres, you can use the indicator variable to ensure that the associated host variable is large enough to hold the full length of the returned character string.
The base type for an indicator variable must be the integer type short. Any type specification built up from short is legal, for example:
## short ind; /* Indicator variable */
## typedef short IND;
## IND ind_arr[10]; /* Array of indicators */
## IND *ind_ptr; /* Pointer to indicator */
Assembling and Declaring External Compiled Forms - VMS only
You can pre-compile your forms in the Visual Forms Editor (VIFRED). By doing so, you save the time otherwise required at runtime to extract the form's definition from the database forms catalogs. When you compile a form in VIFRED, VIFRED creates a file in your directory describing the form in the VAX-11 MACRO language. VIFRED prompts you for the name of the file on which to write the MACRO description. After the file is created, you can assemble it into a linkable object module with the VMS command:
macro filename
The result of this command is an object file containing a global symbol with the same name as your form.
Before the EQUEL/FORMS addform statement can refer to this global object, you must declare it to EQUEL with the following syntax:
globalref int *formname;
Syntax Notes:
• The formname is the actual name of the form. VIFRED gives this name to the variable holding the address of the global object. The formname is also used as the title of the form in other EQUEL/FORMS statements. In all statements that use the formname as an argument, except for addform, you must dereference the name with #.
• The globalref storage class associates the object with the external form definition.
• Although you declare formname as a pointer, you should not precede it with an asterisk when using it in the addform statement. The following example shows a typical form declaration and illustrates the difference between using the form's object definition and the form's name. For example:
## globalref int *empform;
## addform empform; /* The global object */
## display #empform;
/* The name of the form must be dereferenced
** because it is also the name of a variable */
Compiling and Declaring External Compiled Forms -UNIX only
You can precompile your forms in VIFRED. This saves the time that would otherwise be required at runtime to extract the form's definition from the database forms catalogs. When you compile a form in VIFRED, VIFRED creates a file in your directory describing the form in C. VIFRED prompts you for the name of the file with the description. After the file is created, you can use the following cc command to compile it into linkable object code:
cc -c filename
The C compiler usually returns warning messages during this operation. You can suppress these, if you wish, with the -w flag on the cc command line. This command produces an object file containing a global symbol with the same name as your form.
Before the EQUEL/FORMS statement addform can refer to this global object, you must declare it to EQUEL with the following syntax:
extern int *formname;
Syntax Notes:
• The formname is the actual name of the form. VIFRED gives this name to the variable holding the address of the external object. The formname is also used as the title of the form in other EQUEL/FORMS statements. In all statements that use the formname as an argument, except for addform, you must dereference the name with #.
• The extern storage class associates the object with the external form definition.
• Although you declare formname as a pointer, you should not precede it with an asterisk when using it in the addform statement.
The following example shows a typical form declaration and illustrates the difference between using the form's object definition and the form's name.
## extern int *empform;
## addform empform; /* The global object */
## display #empform;
## /* The name of the form must be dereferenced */
## /* because it is also the name of a variable */
Concluding Example
The following example demonstrates some simple EQUEL/C declarations:
## define MAX_PERSONS 1000
## typedef struct datatypes_ /* Structureof all types */
## {
## char d_byte;
## short d_word;
## long d_long;
## float d_single;
## double d_double;
## char *d_string;
## } datatypes;
## datatypes d_rec;
## char *dbname = "personnel";
## char *formname, *tablename, *columnname;
## varchar struct {
## short len;
## char binary_data[512];
## } binary_chars;
## enum color {RED, WHITE, BLUE};
## unsigned int empid;
## short int vac_balance;
## struct person_ /* Structure with a union */
## {
## char age;
## long flags;
## union
## {
## char full_name[30];
## struct {
## char firstname[12],
## lastname[18];
## } name_parts;
## } person_name;
## } person, *newperson, person_store[MAX_PERSONS];
UNIX:
## extern int *empform,*deptform; /* Compiled forms */
VMS:
## globalref int *empform, *deptform; /*Compiled forms*/