# CHAPTER 2: The mx Routines

## 2.1 Introduction

The mx routines are classified into four groups:

1. Routines that open and close tables.

2. Qualification building routines.

3. Record retrieval routines based on qualifications.

4. Record manipulation routines.

The first group includes routines that open and close table files. A table must be opened before it can be read or updated. Opening a table will also lock it at the level indicated in the data dictionary for the database. Any tables which have been opened should be closed before the program ends, and this will also unlock them.

The second group of routines is used to specify a subset of a table. This is analogous to the construction of a `WHERE` clause in the Query Language.

The third group of routines is used to retrieve records from one or more tables. Any qualifications which may have been constructed are used by these routines. The retrieval routines will automatically use any appropriate indices present to improve retrieval speed.

The fourth group is used to add to, delete from, and update tables.

## 2.2 Accessing the mx Routines

### 2.2.1 The Header File mscc.h

The header file `mscc.h` must be included at the beginning of each program using the mx routines. It has the correct definitions for all the mx (and mr) routines and a number of useful definitions of data types and constants. `mscc.h` also includes the Standard I/O Library, so there is no need to include this in your programs. The file is shown below.

Please note that the `mscc.h` file differs from version to version. For an exact reference, the file is located in the following `$EMPRESSPATH/include` directory.

       /***********************************************************************
        *      (c) Copyright   Empress Software Inc. 1983, 2003
        ***********************************************************************/

       #ifndef __MSCC_H

       #define __MSCC_H

       #ifdef __cplusplus
       extern "C" {
       #endif

       #include        <stdio.h>
       #include        "c.h"

       #include        <misc/public.h>
       #include        <usrmp/msdsqldt.h>
       #include        <usrfns/dtparvar.hx>
       #include        <api/mrapi.hx>
       #include        <api/msapi.hx>
       #include        <api/mxapi.hx>
       #include        <misc/init.hx>
       #include        <misc/mscc_com.h>
       #include        <misc/mxe.h>
       #include        <misc/mxtrig.h>

       #ifdef __cplusplus
       }
       #endif

       #endif  /* __MSCC_H */

### 2.2.2 Initialization and Cleanup

There are two ways to organize a program using the Empress Host Language Interface routines:

1. You can name the regular main procedure `msmain`. This will include a default `main` procedure which does the necessary initializations. `msmain` may have the parameters `argc` and `argv` as usual. Similarly, name the regular C exit procedure `msexit`.

2. You can write your own `main` procedure, calling `msinit` to begin and `msexit` to end. These two procedures carry out various initialization and cleanup tasks.

   The `msinit` can be called several times during the execution of a program. Subsequent `msinit` calls should be preceeded, in general case, by a cleanup task (i.e. `msend`). However, this is not neccessary, and spawned processes can invoke `msinit` although `msend` hasn't been called previously.

The routine `msclean` allows you to put a procedure on the stack to be called immediately before the program exits.

### 2.2.3 Compiling and Linking

Programs using the mx routines must be compiled using `empcc` or `empc++`. The effect of this is to ensure that Empress library routines are included in the compilation; in all other respects, for example, `empcc` behaves similarly to `cc`.

Note that `mscall` statements may also be used in programs compiled by `empcc` or `empc++`. For example, the simplest way to create a new table in an application program is to use an `mscall` statement.

## 2.3 Internal, External, and File Attribute Formats

Throughout this manual, references will be made to internal, external, and file formats of attribute values.

The external format is what is printed by a Query Language `SELECT` command. External format values are character strings.

The internal format is the format in which the value is manipulated by Empress, and does not necessarily correspond to the external format. For example, `date (1)` values are not manipulated as (for instance) "1 May 1981'', nor are dollar values manipulated complete with dollar signs, asterisks, and commas.

Finally, there is the file format, which is the way the value is actually stored by Empress. Application programs do not retrieve file format values.

Attribute values are normally retrieved in external format, though it is possible to retrieve integers and internal format values directly. Values are normally assigned to attributes in external format or as integers, though it is possible to assign an internal format value directly to an attribute.

To do arithmetic on external format (character string) retrieved values, they must first be converted to numeric values using `sscanf`, `atof`, etc. Note that dollar signs, asterisks, and so on must be removed from dollar values before converting them. A simple procedure for doing this, `dollcvt`, is presented in the example program `interest.c` in the next chapter.

Bulk data values are handled differently from other types. To output bulk values in external format (as in a Query Language `SELECT`) would require converting the bytes to hexadecimal characters, which is not what is generally needed. Instead, bulk data is normally output in internal format using the routine `mxcopyi`. This routine is intended specifically for retrieving bulk data, though it can be used to get the internal format of any other type of data as well. The routine `mxputi` is intended for placing bulk data into a record.

## 2.4 Handling Normal Failures: mxerret and mxoperr

There are situations in which the routines are properly unable to execute successfully, such as when record level locking is in force for a table and a record which the routine needs to access is locked, or when the data which a routine attempts to insert into a table is disallowed because of range checking or a limited duplicate index, or when inappropriate data is passed to one of the qualification routines.

Most of the mx routines return a value of true or false, depending on whether they succeed. What happens on failure depends on the value of the variable `mxerret`. If this is 0, the calling program is terminated with a suitable error message; if it is 1, the calling program is not terminated, the variable `mxoperr` is set to one of the `MRER`... codes listed in the header file `mrerrno.h`, and control is returned to the program. `mxoperr` may be examined by the application program to determine why the routine fails, and something sensible done about it.

The default value for `mxerret` is 0, so application programs which do something about failures must set it to 1.

The mx routines that do not return true or false print an error message and terminate the calling program when they fail.

The `mrerrno.h` is listed below. Please note that this file differs from version to version. For an exact reference, the file is located in the `$EMPRESSPATH/include/usrmr` directory.

          /***********************************************************************
           *  (c) Copyright Empress Software Inc. 1983, 2003
           ***********************************************************************/

          #define MRNOERR             0 /* no error indication*/
          #define MRERNAME            1 /* invalid table name */
          #define MREREXIST           2 /* table already exists */
          #define MRERNEXIST          3 /* table does not exist */
          #define MRERRPERM           4 /* no read permission on table */
          #define MRERWPERM           5 /* no read/write permission on tab */
          #define MRERBUSY            6 /* database/table lock busy */
          #define MRERVWOP            7 /* operation invalid on view */
          #define MRERVWDEF           8 /* invalid definition for view */
          #define MRERVWRECUR         9 /* recursive definition for view */
          #define MRERDBEXIST        10 /* not a database */
          #define MRERANAME          11 /* invalid attribute name */
          #define MRERAEXIST         12 /* attribute already exists */
          #define MRERANEXIST        13 /* attribute does not exist */
          #define MRERDTPAR          14 /* invalid data type/parameters */
          #define MRERINAME          15 /* invalid index name */
          #define MRERNULL           16 /* null value for attribute */
          #define MRERUDUP           17 /* duplicate value for attribute */
          #define MRERDUP            18 /* too many duplicate values */
          #define MRERCVT            19 /* bad value conversion for attr */
          #define MRERPRIV           20 /* no privilege for table */
          #define MRERAPRIV          21 /* no update privilege on attribute */
          #define MRERCRPRIV         22 /* no create table privilege */
          #define MRERQOP            23 /* invalid operator/function */
          #define MRERQATTR          24 /* incompatible attributes */
          #define MRERQPAT           25 /* invalid pattern */
          #define MRERQRNG           26 /* invalid range */
          #define MRERXCONS          27 /* invalid constant */
          #define MRERXCVT           28 /* invalid conversion */
          #define MRERXFUNC          29 /* invalid operator/function */
          #define MRERIEXIST         30 /* index already exists */
          #define MRERINEXIST        31 /* index does not exist */
          #define MRERICOMP          32 /* index name/definition mismatch */
          #define MRERCREATOR        33 /* invalid creator name */
          #define MRERCRPERMS        34 /* invalid create permissions */
          #define MRERDDEXIST        35 /* data dictionary already exists */
          #define MRERDBA            36 /* not the dba */
          #define MRERINPRIV         37 /* insufficient privileges */
          #define MRERDBACR          38 /* not the dba or creator */
          #define MRERRCNAME         39 /* invalid constraint name */
          #define MRERRCEXIST        40 /* constraint already exists */
          #define MRERRCNEXIST       41 /* constraint does not exist */
          #define MRERRCINSERT       42 /* insert constraint violation */
          #define MRERRCDELETE       43 /* delete constraint violation */
          #define MRERRCLKBUSY       44 /* cannot check constraint */
          #define MRERRCINCDTS       45 /* incompatible types in constraint */
          #define MRERRCOTABLE       46 /* cannot open table for constraint */
          #define MRERRCNATTR        47 /* invalid attribute in constraint */
          #define MRERNLOC           48 /* invalid location */
          #define MRERVWTWICE        49 /* table selected twice in view */
          #define MRERVWNONEQ        50 /* join condition without '=' */
          #define MRERVWOROP         51 /* OR conjunction in view */
          #define MRERVWNOTOP        52 /* NOT operator in view */
          #define MRERVWJOIN         53 /* insufficient join conditions */
          #define MRERVWKEY          54 /* insufficient keys in view */
          #define MRERVWREATTR       55 /* view attributes repeated/equated */
          #define MRERVWNONULL       56 /* cannot obtain 'not null' value */
          #define MRERVWUNIND        57 /* unique value unobtainable in view */
          #define MREREXPR           58 /* expression failure */
          #define MRERTRPROGRESS     59 /* transaction already in progress */
          #define MRERTRSTARTNOT     60 /* unable to start transaction */
          #define MRERTROFF          61 /* no transaction in progress */
          #define MRERTRINVSVP       62 /* invalid save point name */
          #define MRERTRSVPEXISTS    63 /* save point name already exists */
          #define MRERTRNOSVP        64 /* save point does not exist */
          #define MRERNUPDMODE       65 /* table opened in non-updatable mode */
          #define MRERSERVER         66 /* server not available */
          #define MRERANOTDTF        67 /* not a variable length attribute */
          #define MRERINOVRFL        68 /* index has no overflow file */
          #define MRERCRFL           69 /* cannot create data files */
          #define MRERCRFLACCESS     70 /* no access to create data files */
          #define MRERVWATTREX       71 /* too many attribute names specified */
          #define MRERGENDT          72 /* unknown generic type */
          #define MRERRECNO          73 /* invalid record number */
          #define MRERDT             74 /* invalid data type */
          #define MREREXDTPAR        75 /* too many parameters for the datatype */
          #define MRERBADDTPAR       76 /* bad datatype parameter */
          #define MRERATTRSPEC       77 /* attribute specification not allowed */
          #define MRERHAVBADA        78 /* disallowed attribute in having clause */
          #define MRERVWGRPGRP       79 /* no group by allowed on grouped view */
          #define MRERVWGRPHAV       80 /* no having allowed on grouped view */
          #define MRERVWGRPJOIN      81 /* no join allowed on a grouped view */
          #define MRERVWGRPQUAL      82 /* no qual allowed on grouped view */
          #define MRERVWORDERINT     83 /* invalid order by specification (integer) */
          #define MRERVWINSELHAV     84 /* invalid select list with having clause */
          #define MRERQNESTATTR      85 /* too many attributes in nested select */
          #define MRERVWAGGATTRX     86 /* no derived aggr attribute in expression */
          #define MRERVWAGGRHAV      87 /* no having allowed on aggregate view */
          #define MRERVWAGGRJOIN     88 /* no join allowed on a aggregate view */
          #define MRERVWAGGRQUAL     89 /* no qual allowed on aggregate view */
          #define MRERVWGRPAGGR      90 /* no aggr function allowed on grouped view */
          #define MRERVWINSELDIST    91 /* invalid select list with select distinct */
          #define MRERVWAGGATTRG     92 /* no derived aggr attribute in group by */
          #define MRERNOHASHREC      93 /* record not found in table */
          #define MREROPMODE         94 /* invalid table open mode */
          #define MRERVWORDERSEL     95 /* invalid order by specification (select) */
          #define MRERINVTXT         96 /* invalid char in the value for attribute */
          #define MRERDRIOATTR       97 /* direct I/O not supported for attribute */
          #define MRERDRIOSET        98 /* file redirection error */
          #define MRERCRSIZE         99 /* record size too large */
          #define MRERNLCOMP        100 /* null compile entry for the table */
          #define MRERNOCOMP        101 /* no compile entry for the table */
          #define MRERNVEW          102 /* expecting a view, got a table */
          #define MR_DB_ALREADY_LOCKED 103 /* the database has been already locked */
          #define MR_IN_ADM         104 /* the dba put the database down*/
          #define MRERDTER          105 /* Refer to error at DT level */
          #define MRERINVUREC       106 /* invalid record for update */
          #define MRERURECDEL       107 /* record for update has been deleted */
          #define MRERAGGRNORECS    108 /* no records to do aggregate */
          #define MRERNOCRSHM       109 /* cannot create shared memory */
          #define MRERNORMSHM       110 /* cannot remove shared memory */
          #define MREROUTERILL      111 /* Illegal outer join */
          #define MRERNOSHM         112 /* No shared memory available */
          #define MRERDRECDEL       113 /* deleted record for delete */
          #define MRERNONEXIST      114 /* no data file for '%s' */
          #define MRERNET           115 /* network error in file operation */
          #define MREROPEN          116 /* bad data file for '%s' */
          #define MRERFLOPMODE      117 /* invalid open mode '%c' */
          #define MRERSVPORT_BADID  118 /* invalid server port id */
          #define MRERSVPORT_INCOMPAT 119 /* incompatible server port id */
          #define MRERSVPORT_UNDEF  120 /* server port not defined */
          #define MREROPDICTMODE    121 /* invalid data dictionary open mode */
          #define MRERFULLBUSY      122 /* lock resource is full */
          #define MRERNOLKRESOURCE  123 /* cannot access lock resource */
          #define MRERBADRECORD     124     /* bad record retreived */
          #define MRERBADDATARETR   125     /* from DT: bad var.len.val.retreived*/
          #define MRERSORTLOCK      126 /* sort by error(locked) **WARNING */

          #define MRERMNAME         127 /* invalid module name */
          #define MRERMEXIST        128 /* module already exist */
          #define MRERMNEXIST       129 /* module does not exist */
          #define MRERLANGOPTION    130 /* invalid language option */
          #define MRERFNAME         131 /* invalid SQL-invoked routine name */
          #define MRERFMCLASS       132 /* there are multiple function
                                           classes for same routine */
          #define MRERFEXIST        133 /* SQL-invoked routine already exist */
          #define MRERFNEXIST       134 /* SQL-invoked routine not exist */
          #define MRERPARAMNAME     135 /* invalid parameter name */
          #define MRERPARAMEXIST    136 /* parameter already exist */
          #define MRERPARAMNEXIST   137 /* parameter does not exist */
          #define MRERPARAMNO       138 /* invalid parameter number */
          #define MRERQPARAM        139 /* incompatible parameter */
          #define MRERPARAMNUM      140 /* too many parameters */
          #define MRERPARAMNUM1     141 /* expecting one parameter */
          #define MRERPARAMNUM2     142 /* expecting two parameters */
          #define MRERPARAMB        143 /* parameter data type should
                                           be generic boolean */
          #define MRERPARAMNB       144 /* parameter data type should
                                           be parameteric data type */
          #define MRERRETB          145 /* return data type should be
                                           generic boolean */
          #define MRERRETNB         146 /* return data type should be
                                           parametric data type */
          #define MRERARETNB        147 /* aggregate function's return data type
                                           should be parametric data type */
          #define MREREXTNAME       148 /* invalid external function name */
          #define MREREXTEXIST      149 /* external name already exist */
          #define MRERFILENEXIST    150   /* file doesn't exist */

          #define MRERTRNAME        151  /* invalid trigger name */
          #define MRERTREXIST       152 /* trigger already exist */
          #define MRERTRNEXIST      153 /* trigger does not exist */
          #define MRERMODWRAPCOMPFAIL 154 /* failed to compile wrapper for module */
          #define MRERDDCACHEFAIL   155 /* failed to modify DD_CACHE for module */
          #define MRERXSVFUNC       156 /* SQL-invoked routine '%s' is not
                                           available on the client */
          #define MRERTRIGABORT     157 /* operation aborted by trigger */
          #define MRERXTRIGPROC     158 /* trigger procedure should have
                                           no parameter */
          #define MRERNOTRIGPROC    159 /* trigger procedure '%s' unavailable */
          #define MRERTRIG_OLD_NEW  160 /* attribute '%s' must be qualified
                                           by OLD or NEW */
          #define MRERTRIGNESTED    161 /* nested queries are not allowed
                                           for triggers */
          #define MRERPARAMSTYLE    162 /* parameter style of SQL-invoked
                                           routine '%s' inconsistent */
          #define MRERMODWRAPGENFAIL 163 /* failed to generate wrapper for system */
          #define MRERNOATTR        164 /* no attributes left in table '%s */
          #define MRERRNGCONVT      165 /* cannot convert range check for
                                           attribute '%s' */
          #define MRERCANTADDATTR   166 /* cannot add non-null attribute
                                           to non-empty table */
          #define MRERDUPATTR       167 /* duplicate attribute '%s' */
          #define MRERFEWCONS       168 /* too few constants */
          #define MRERMANYCONS      169 /* too many constants */
          #define MRERWRONGCONS     170 /* wrong number of constants */
          #define MRERAINVEXPR      171 /* invalid expression for attribute '%s' */
          #define MRERSAMETAB       172 /* same table involved in select
                                           and insert */
          #define MRERFEWSETATTR    173 /* too few SET attributes */
          #define MRERMANYSETATTR   174 /* too many SET attributes */
          #define MRERFEWATTRS      175 /* too few attributes in '%s' */
          #define MRERNOTNULLATTR   176 /* not-null attribute '%s' is not
                                           included in attribute list */
          #define MRERINVATTR       177 /* invalid attribute '%s' for table '%s' */
          #define MRERINVIDXPARM    178 /* invalid index parameters (%s, %s)" */
          #define MRERNOPRIVCRVW    179 /* insufficient privileges to create view */
          #define MRERUNEQCONSTR    180 /* unequal number of attributes
                                           in the constraint */
          #define MRERNOCONSTR      181 /* constraint '%s' does not exist */
          #define MRERNOIDX         182 /* index '%s' does not exist */
          #define MRERSYSTABLE      183 /* cannot perform %s operation on %s */
          #define MRERINVOPR        184 /* invalid operator '%s' */
          #define MREREXPRPROB      185 /* expression cannot be evaluated
                                           or out of range */
          #define MRERUPDMOD        186 /* failed to update module '%s'
                                           (unable to resolve symbol) */
          #define MRERCOPYRL        187 /* failed to copy file from server
                                           to client */
          #define MRERCOPYLR        188 /* failed to copy file from client
                                           to server */
          #define MRERINVCALLEXPR   189 /* invalid call expression, output
                                           is not bound with a variable */
          /* next number is 190 */

## 2.5 Empress File Quotas

If you need to open your own files in your program and cannot do so because Empress has used all available file descriptors, you will have to limit the number of files Empress will attempt to open. This can be done by setting the Empress system variable `MSFILESOPEN` in the operating system. For example, to limit the number of files Empress will attempt to open to 10:

- In UNIX C shell,

         setenv MSFILESOPEN 10

- In UNIX Bourne shell,

         MSFILESOPEN = 10
         export MSFILESOPEN

- In Windows NT,

         set MSFILESOPEN = 10

Not that, this will not prevent Empress from accessing more than 10 files. Empress will close the least-recently used file before opening another file, if the quota is reached. If the file that was closed by Empress is accessed again, Empress will automatically re-open it. Empress keeps track of these files internally and the closing and re-opening of files are transparent to the user.

For details on setting Empress system variables, refer to the Setting Variables in the Empress SQL: User's Guide.

## 2.6 Naming Conventions

The Empress mx routines have been named in a logical manner to indicate their functions. Since external names in C are limited, the mx routine names are by necessity somewhat abbreviated. Each routine name begins with "mx''. Most then have a third generic letter, such as "`q`'' for the routines dealing with qualifications, or a full word, such as "`get`'' for the routines which find attribute values. A classification and list of all the mx routines is shown on the next page. Manual pages with detailed specifications for each of these routines may be found at the end of the manual, and should be referred to when writing programs.

The following table lists the routines used to open and close tables:

Table 2-1: Open and Close Routines

Function | Purpose
`mxopen` | Open a table.
`mxclose` | Close a table.
`mxaclose` | Close all tables and de-allocates storage.

The following table lists the routines used to check attributes:

Table 2-2: Check Attributes Routines

Function | Purpose
`mxchkattr` | Check whether an attribute exists.
`mxigeta` | Find an attribute name given its number.

The following table lists the routines used to retrieve attribute values:

Table 2-3: Retrieval Routines

Function | Purpose
`mxgetbegin` | Initialize retrieval routines
`mxsrtbegin` | Initialize retrieval routines for sorted output.
`mxget` | Retrieve the next record.
`mxreget` | Try again to retrieve the next record.
`mxprev` | Retrieve the previous record.
`mxreprev` | Try again to retrieve the previous record.
`mxgetend` | Terminate retrieval routines
`mxgetvs` | Get an external format attribute value.
`mxgetvi` | Get an integer attribute value.
`mxcopyi` | Get an internal format attribute value.

The following table lists the routines used to modify attribute and record values:

Table 2-4: Update Routines

Function | Purpose
`mxadd` | Insert a record into a table.
`mxdel` | Delete a record from a table.
`mxput` | Update a record in a table.
`mxputvs` | Put an external format value into an attribute.
`mxputvi` | Put an integer value into an attribute.
`mxputi` | Put an internal format value into an attribute.
`mxsetnull` | Set all attributes in a record to `NULL`.

The following table lists the routines used to build qualifications for data retrieval:

Table 2-5: Qualification Routines

Function | Purpose
`mxqcon` | Compare an attribute with an external format constant.
`mxqconi` | Compare an attribute with an internal format constant.
`mxqrng` | Compare an attribute with a range (external format).
`mxqrngi` | Compare an attribute with a range (internal format).
`mxqatr` | Compare two attribute values.
`mxqmch` | Match an attribute value with a pattern.
`mxqnul` | Compare an attribute value with `NULL`.
`mxqand` | Perform an `AND` on two qualifications.
`mxqor` | Perform an `OR` on two qualifications.
`mxqnot` | Negate a qualification.
`mxqieq` | Qualify attributes equal to an integer value.
`mxqseq` | Qualify attributes equal to a string value.
`mxcompare` | Test if a constant is "`=`", "`>`", or "`<`" an attribute value.

The following table lists the routines used to control transactions:

Table 2-6: Transaction Routines

Function | Purpose
`mxtrstart` | Start a transaction.
`mxtrcancel` | Cancel a transaction.
`mxtrcommit` | Commit a transaction.
`mxtrsave` | Set a save point.
`mxtrrollback` | Roll back to save point.

## 2.7 Empress Internal Procedure Names

When writing applications programs, if one of your own procedures has the same name as one of the Empress internal procedures, problems occur (e.g., complaints from the loader about multiply-defined routines). To help spot this problem, the prefixes used in naming the Empress internal procedures are listed below. (This is not a complete list and it may change from version to version of Empress.) If you are having problems with one of your routines and its name begins with one of these prefixes, try changing its name.

| `addr` | `ap*` |  |  |  |  |  |  |
| `boolean` | `byte` |  |  |  |  |  |  |
| `cpbytes*` | `cv*` |  |  |  |  |  |  |
| `date*` | `day*` | `dt*` |  |  |  |  |  |
| `er*` | `ewrite*` | `exit*` | `extsrt*` |  |  |  |  |
| `fl*` | `fm*` | `fork*` |  |  |  |  |  |
| `gdat*` | `gdec*` | `gflt*` | `gint*` |  |  |  |  |
| `ll*` |  |  |  |  |  |  |  |
| `main` | `match*` | `mem*` | `month*` | `mp*` | `mr*` | `ms*` | `mw*` | `mx*`
| `of*` |  |  |  |  |  |  |  |
| `pos` | `prog*` |  |  |  |  |  |  |
| `ql*` |  |  |  |  |  |  |  |
| `ran*` |  |  |  |  |  |  |  |
| `sc*` | `sep*` | `sfmt` | `sig*` | `sm*` | `sp*` | `str*` |  |
| `taddr` | `time*` | `tty*` |  |  |  |  |  |
| `ufx*` |  |  |  |  |  |  |  |
| `varg*` |  |  |  |  |  |  |  |
| `wkday*` | `word` | `ww*` |  |  |  |  |  |
| `xf*` |  |  |  |  |  |  |  |
| `year*` |  |  |  |  |  |  |  |

## 2.8 General Overview of the mx Routines

Given a database directory, a table within it, and a code for the required open mode, `mxopen` opens the table and locks it at the level indicated in the data dictionary for the database.

The presence or absence of a particular attribute within a table may be checked by the routine `mxchkattr`.

Record retrievals must be initialized by calling `mxgetbegin`, which associates any qualification (an internal representation of a `WHERE` clause) with the record. (`mxgetbegin` must be called even if there is no qualification.) If sorting is desired, `mxsrtbegin` should be used instead of `mxgetbegin`.

Retrievals are accomplished by repeated calls to `mxget` and `mxprev`. Indices will automatically be used to improve retrieval speed if they are present. `mxgetvs` is used to transfer an attribute value to user working storage. This produces a character string representation of the data, which may be examined and manipulated by the user's program. `mxgetend` must be called to clean up after retrievals.

To create a new record, attribute values are added to a record using `mxputvs`, and an entire record may be set to `NULL` using `mxsetnull`. The record is then added to the table by a call to `mxadd`.

Records are deleted using `mxdel`.

Records are updated using `mxputv` to change attribute values in a previously-retrieved record, and `mxput` to replace the old record by the new one.

Qualifications are created by calls to `mxqcon`, `mxqconi`, `mxqrng`, `mxqrngi`, `mxqatr`, `mxqmch`, `mxqnul`, `mxqieq`, or `mxqseq`, which compare an attribute value with a constant, a range of values, or another attribute value, perform a match with a constant, a comparison with `NULL`, an "equals'' comparison with an integer, or an "equals'' comparison with a string, respectively. Complex qualifications are produced using `mxqand`, `mxqor`, or `mxqnot`, which perform `AND` and `OR` functions and negate another qualification, respectively.

If you simply wish to see whether a value in external format is equal to, less than, or greater than an attribute value, the routine `mxcompare` may be used.

When all operations on a table have been finished, it should be closed using `mxclose`. Note that it is good practice to close tables once they are no longer needed; otherwise, the application may run out of space.

If an application program terminates abnormally due to some error condition, such as receiving an interrupt, the routine `mxaclose` may be called to close all open tables and de-allocate all mx-used storage. This routine should not be used indiscriminately in place of `mxclose` and `mxgetend`, however, as using it defeats all the normal error-checking done by the mx routines. (If an application program has failed to close tables or de-allocate storage, an error message warning that space is still allocated is normally printed when the program is run.)

A transaction may be started by `mxtrstart`, committed by `mxtrcommit`, or cancelled by `mxtrcancel`. (Note that a transaction may not be started, committed, or cancelled in the middle of a retrieval loop.) A save point in a transaction is set with `mxtrsave` and a transaction is rolled back to a save point with `mxtrrollback`.

## 2.9 Database, Table, and Attribute Names

Most arguments to the mx routines are used to specify database, table, or attribute names, or data values. Generally, these arguments are character strings.

When two or more opened databases have tables with the same name, the database name and a colon are prefixed to the table name, thus:

       database:table

Attribute names can be similarly qualified by prefixing a table name and a period to the attribute name. This may be further qualified by a database name as before. Hence, acceptable forms of attribute names are:

       attribute
       table.attribute
       database:table.attribute

It is good practice to qualify attribute names with their table names as a matter of course, since errors resulting from dealing with an attribute in the wrong table may be quite difficult to spot when debugging code. This also guards against confusion resulting from later attribute name changes in the tables used by the application.

## 2.10 Error Messages

Several Empress-related errors are liable to occur when using the mx routines. There are five major types of errors:

- program bugs
- space problems
- user errors
- database problems
- file problems

All have the following format:

       *** Error Type *** explanation_of_error

Program Bug

A program bug error message indicates that there is a programming mistake in the applications program, and usually arises from passing a routine the wrong parameters. As another example, attempting to insert data into a table which was opened in read-only mode will also cause program bug errors.

Space Problem

This kind of error simply indicates that the program has run out of space. If you get this error, check to see that any open tables are closed once you no longer need them.

User Error

This kind of error is not strictly related to the use of the mx routines themselves, but is more likely to result from data input by the users who are running the applications. It is generally caused by passing incorrect table or attribute names to a routine. Incorrect names which are written into an applications program are usually easy to find and fix. The routine `mxchkattr` can be used to check the validity of attribute names.

Database Problem

This error is very serious, and occurs when the database is found to be corrupted.

File Problem

This error occurs when the mx routines cannot access a file which they should be able to access. It is generally a serious problem, and may indicate a corrupted database.

## 2.11 Locking

If locking is specified for a table in the data dictionary of the database, the table or its records can be locked in read or update mode. If no lock level is specified in the data dictionary, no locks can be placed on the table/records.

Locking a table or record in read mode means that others may access it, provided they also access it in read mode. Thus, anyone who wishes to update this table or record will be prevented from accessing it until everyone is finished with the table.

Locking a table or record in update mode means no one else may access it for reading or updating. This can cause a bottleneck if one person keeps the table or record locked for a long period of time, or if a table or record is locked and never unlocked.

A table can be opened in read, update or deferred mode. Opening a table in read mode places read locks on the table. Opening a table in update mode places update locks on the table. Opening a table in deferred mode places read locks on the table initially, but these locks are exchanged for update locks when the table is written to.

A table level lock takes effect when a table is opened. A group level lock takes effect when an `mxgetbegin` is executed. A record level lock takes effect when an `mxget` is executed.

Some applications, especially those involving a read-only database or a "single-user'' database, do not require locks. These applications are better implemented without using locks, since employing the locking mechanism introduces complexities and is hence more error-prone. The Database Administrator should set guidelines for the use or non-use of locks, and set the default locking on tables accordingly. For a discussion on locking, see the Empress SQL: Reference manual under the `LOCK LEVEL` command.

The utility program `empclrlk` and `empadm` may be used to clean up any hanging locks on a database. This is particularly useful when debugging programs with `MSDEBUG` set to abort, because if a program core dumps you will almost certainly have left-over locks which should be removed.

## 2.12 Portability

Executable programs containing calls to mx routines can be moved across machines. However, execution errors will result if the installed Empress path is different across machines. In this case, programs should be re-compiled on the target machine.