





          Typhoon Relational Database Management System

                          User's Manual










1 INTRODUCTION. . . . . . . . . . . . . . . . . . . . . . . .   3

2 DATABASE DEFINITION . . . . . . . . . . . . . . . . . . . .   3
      2.1 Grammar . . . . . . . . . . . . . . . . . . . . . .   3
      2.2 Files . . . . . . . . . . . . . . . . . . . . . . .   5
      2.3 Tables. . . . . . . . . . . . . . . . . . . . . . .   5
      2.4 Key declarations. . . . . . . . . . . . . . . . . .   7
             2.4.1 Primary. . . . . . . . . . . . . . . . . .   7
             2.4.2 Alternate. . . . . . . . . . . . . . . . .   7
             2.4.3  Foreign . . . . . . . . . . . . . . . . .   8
      2.5 Sorting map . . . . . . . . . . . . . . . . . . . .   8

3 TOOLS . . . . . . . . . . . . . . . . . . . . . . . . . . .   9
      3.1 Data Definition Language Processor. . . . . . . . .   9
      3.2 Database Definition Viewer. . . . . . . . . . . . .   9
      3.3 Export tool . . . . . . . . . . . . . . . . . . . .   9
      3.4 Import tool . . . . . . . . . . . . . . . . . . . .   9

4 APPLICATION PROGRAMMING INTERFACE . . . . . . . . . . . . .  11
      4.1 Currency concept. . . . . . . . . . . . . . . . . .  11
      4.2 Status codes. . . . . . . . . . . . . . . . . . . .  11
      4.3 Opening and closing . . . . . . . . . . . . . . . .  12
             4.3.1 d_open . . . . . . . . . . . . . . . . . .  12
             4.3.2 d_close. . . . . . . . . . . . . . . . . .  12
             4.3.3 d_dbget. . . . . . . . . . . . . . . . . .  13
             4.3.4 d_dbset. . . . . . . . . . . . . . . . . .  13
             4.3.5 d_dbdpath. . . . . . . . . . . . . . . . .  14
             4.3.6 d_dbfpath. . . . . . . . . . . . . . . . .  14
             4.3.7 d_setfiles . . . . . . . . . . . . . . . .  15
      4.4 Record operations . . . . . . . . . . . . . . . . .  15
             4.4.1 d_fillnew. . . . . . . . . . . . . . . . .  15
             4.4.2 d_recwrite . . . . . . . . . . . . . . . .  16
             4.4.3 d_recread. . . . . . . . . . . . . . . . .  17
             4.4.4 d_delete . . . . . . . . . . . . . . . . .  17
             4.4.5 d_recfrst. . . . . . . . . . . . . . . . .  18
             4.4.6 d_reclast. . . . . . . . . . . . . . . . .  19
             4.4.7 d_recnext. . . . . . . . . . . . . . . . .  19
             4.4.8 d_recprev. . . . . . . . . . . . . . . . .  20
             4.4.9 d_crget. . . . . . . . . . . . . . . . . .  20
             4.4.10 d_crread. . . . . . . . . . . . . . . . .  21
      4.5 Key operations. . . . . . . . . . . . . . . . . . .  21
             4.5.1 d_keyfind. . . . . . . . . . . . . . . . .  21
             4.5.2 d_keyfrst. . . . . . . . . . . . . . . . .  22
             4.5.3 d_keylast. . . . . . . . . . . . . . . . .  23
             4.5.4 d_keynest. . . . . . . . . . . . . . . . .  24
             4.5.5 d_keyprev. . . . . . . . . . . . . . . . .  24
             4.5.6 d_keyread. . . . . . . . . . . . . . . . .  25

1 INTRODUCTION

      Typhoon is a relational database management system intended for C
programmers on Unix and OS/2 platforms.

      The database tables are defined in a DDL (Data Definition Language)
file which is a plain ASCII file, containing C language structure
declarations. The DDL is processed by a Data Definition Language Processor
(ddlp) which produces a Database Definition file (DBD) and a header file.

      The DBD file contains a description of all database objects; tables,
fields, indexes and files, and the header file contains C language constants
used in the application program to reference these objects.

                           +------------+
                           |  demo.ddl  |
                           +------------+
                                 |
                                 v

                                ddlp

                               /    \
                              /      \ 
                             v        v
                     +----------+  +------------+
                     |  demo.h  |  |  demo.dbd  |
                     +----------+  +------------+

      The rest of this manual describes how to create databases and how the
C library functions are used.


2 DATABASE DEFINITION

      A database is described in a Data Definition Language file which is an
ASCII file with the extension ".ddl". The file consists of C language
structure declarations that directly reflect the way records are stored in the
database.


2.1 Grammar

      The following Extended BNF grammar describes the syntax of the DDL
file. The grammar is divided into five logical sections.

      File declarations
      Table declarations
      Structure declarations
      Field declarations
      Sorting map

      Some of the keywords, e.g. "record" and "key", may seen a bit strange,
but are called so for historic reasons. The format of the DDL was derived from
db_VISTA's DDL file, which uses these keywords.

      C++ comments (//) and nested C comments (/* .. */) can be used in the
DDL file.


File declarations

database    -> "database" ident '{' decl { decl } '}'

decl        -> file_decl
            |  record_decl
            |  map_decl
            |  "define" ident expr

file_decl   -> "data" "file" [ size ] string "contains" ident ';'
             | "key"  "file" [ size ] string "contains" ident '.' key_type ';'

key_type    -> ident 
            |  "references"


Record declarations

record_decl -> "record" ident '{' member { member } key_decls '}'

member      -> membertype ident [ dimen ] ';'
            |  struct_decl ident [ dimen ] ';'

dimen       -> dimension [ "variable" "by" ident ]

dimension   -> array { array }

array       -> '[' expr ']'


key_decls   -> [ primary_key_decl ]
               { alternate_key_decl }
               { foreign_key_decl }


Key declarations

primary_key_decl
            -> "primary" key_decl ';'

alternate_key_decl
            -> "alternate" [ "unique" ] key_decl [ null_stmt ] ';'

foreign_key_decl
            -> "foreign" key_decl "references" ident
                   "on" "update" action
                   "on" "delete" action [ null_stmt ] ';'

action      -> "restrict"
            |  "cascade"

null_stmt   -> "null" "by" ident


key_decl    -> "key" ident [ '{' key_member {key_member} '}' ]

key_member  -> ident [ "asc" | "desc" ]


Structure member definitions

membertype  -> int_type
            |  int_sign
            |  int_sign int_type
            |  float_type
            |  u_type
            |  "long" float_type

int_type    -> "char"
            |  "short"
            |  "int"
            |  "long"

int_sign    -> "signed"
            |  "unsigned"

float_type  -> "float"
            |  "double"

u_type      -> "uchar"
            |  "ushort"
            |  "ulong"

expr        -> expr '+' expr
            |  expr '-' expr
            |  expr '*' expr
            |  expr '/' expr
            |  '(' expr ')'
            |  number


Struct or union declaration

struct_decl -> struct_or_union [ident] '{' member_list '}'
            |  struct_or_union ident

struct_or_union
            -> "struct"
            |  "union" "controlled" "by" ident


Map declaration

map_decl    -> "map" '{' map { map } '}'

map         -> map_id "->" map_id ';'

map_id      -> "'" char "'"
            |  number



2.2 Files

The file declaration part describes the files that tables and indexes are
stored in. A file can be either a data file or a key file.

file_decl   -> "data" "file" [ size ] string "contains" ident ';'
             | "key"  "file" [ size ] string "contains" ident '.' key_type ';'

key_type    -> ident 
            |  "references"

      The size following the "file" keyword, determines the page size of an
index file or variable length record file. The default page size is 512 bytes.
If an index contains very large keys (e.g. > 40 bytes) the page size should be
set to 1024, 2048 or 4096 to reduce the depth of the B-tree. To gain maximum
efficiency, the number of levels in the B-trees should be kept at minimum,
preferably below 5. The number of keys a page can hold is

      order = (page size - sizeof(long)) / (key size + sizeof(long) * 2)

and the number of levels in a B-tree is

      ln keys / ln order + 1

      For variable length record files, the page size determines the size of
each block a record is divided into. The size should always be greater than
the static part of the record, i.e. the total size of the fixed length fields.

      If a variable length record of a certain type typically is between
1000 and 2000 bytes, the page size should be set to 2048, which means that
most records can be read in a single I/O operation. Records bigger than that
would require two or more I/O operations. It also means that the minimum size
a record of that type will occupy is the page size. Thus, the page size chosen
for a variable length record file, depends very much on the characteristics of
the data stored in it.

      A table that has dependent tables, i.e. tables that reference its
primary key, also needs a "references" file, which specifies the file that
dependencies are stored in for that table's primary key.


2.3 Tables

      The table declaration part describes the actual database, namely the
tables where information is stored in. A record is a sequence of member
declarations followed by a sequence of key declarations. The key declaration
part is described in a subsequent section.

record_decl -> "record" ident '{' member { member } key_decls '}'

member      -> membertype ident [ dimen ] ';'
            |  struct_decl ident [ dimen ] ';'

dimen       -> dimension [ "variable" "by" ident ]

dimension   -> array { array }

array       -> '[' expr ']'

key_decls   -> [ primary_key_decl ]
               { alternate_key_decl }
               { foreign_key_decl }

      The names of records, fields and keys will appear in uppercase in the
header file produced by ddlp. If a field name is used in more than one record
declaration, its name will be preceded by the record name and an underscore,
for example CUSTOMER_NAME.

      The syntax of the record is almost the same as the one for C language
structures. A record can contain chars, ints, longs, floats, arrays and nested
structures and unions. Enumerated types are not supported. The structures are
stored in exactly the same way in the database as in the computer's memory.
For space considerations, structure members should be ordered so that minimum
alignment is done by the compiler. On platforms where a long is four bytes,
struct a would take up 16 bytes, whereas the rearranged struct b only would
take up 10 bytes.

             struct {                        struct ;
                char    a;                      long    b;
                long    b;                      long    d;
                char    c;                      char    a;
                long    d;                      char    c;
             } a;                            } b;

      Typhoon will correctly spot where alignment is done by the compiler,
but there is no need to waste valuable space where not necessary.

      Only the fields in the outermost scope of the record are significant
to Typhoon. In the demo record below, only the members a, b, c, d and x can be
referenced from the application. This means that members in nested structures
or unions cannot be used in key declarations. Nested structures are only
supported to make life easier to the programmer and to enable the tools
tyimport and tyexport to read and write comma-files correctly.

    record demo {
        float   a;
        long    b;
        ushort  x_count;
        char    d[21];
        struct {
            char    e;
            struct {
                union controlled by e {
                    long r;
                    char s[3];
                } u[2];
                long    p;
                int     f[20][2];
                char    g;

            } e[10];
        } x[10] variable by c;

        ...
        ...
    }


      The union declaration differs from its C language equivalent in that
it has a control statement. This is required for tyexport and tyimport to know
which member in a union is active. In the above example, e determines whether
r or s is the active member of u. The members inside a union are numbered from
zero, so if r is active, e must be zero, otherwise 1. The 'control field' must
be a char.

      An array can have more than one dimension, but Typhoon sees it as a
single dimension, since there is no way to reference specific array elements
from the API. An array at the outermost level can have a "variable by"
statement which specifies a member that will be its 'size determinator'. This
member determines the number of elements in the array that should be stored in
the database. The size determinator must be an unsigned short. A record can
contain more than one variable length array which must all be placed at the
end of a record declaration.


2.4 Key declarations

      A table can have one or more key declarations. A primary key, one or
more alternate keys, and one or more foreign keys. The format of the key
declarations are almost the same, but the semantics are quite different.

primary_key_decl
            -> "primary" key_decl ';'

alternate_key_decl
            -> "alternate" [ "unique" ] key_decl [ null_stmt ] ';'


foreign_key_decl
            -> "foreign" key_decl "references" ident
                   "on" "update" action
                   "on" "delete" action [ null_stmt ] ';'

action      -> "restrict"
            |  "cascade"

null_stmt   -> "null" "by" ident


key_decl    -> "key" ident [ '{' key_member {key_member} '}' ]

key_member  -> ident [ "asc" | "desc" ]


2.4.1 Primary

      A table can have at most one primary key, hence its name. The primary
key is used to uniquely identify a record in a table. A primary key can also
be referenced from other tables. These tables are called dependent tables and
the referenced table is called the parent or target table.

      A primary key can consist of more than one field, in which case the
key must be given a unique name. The employee_key below also specifies that
salary should be sorted in descending order.

      primary key employee_key { name, salary desc };


2.4.2 Alternate

      If a table needs more indexes than the primary, one or more alternate
indexes can be defined. These indexes can contain duplicate values. The use of
non-unique indexes is discouraged, however, since they are slower and often
reflect a poor design. However, there are situations where duplicates are a
necessary evil and therefore Typhoon supports them.

      An alternate key can null, in which case it is not stored in the
index. If the field is a string it can be the null indicator itself, otherwise
it must be another char field. If the null indicator is zero, the key is also
null.

      The following example shows a table that contains accounts. If an
account is blocked, blocked_on_date contains the date on which it was blocked.
That way, only blocked accounts will be available through the block_on_date
index.

    record account {
        long    blocked_on_date;
        long    balance;
        char    name[21];
        char    is_blocked;

        primary key id;
        alternate key blocked_on_date null by blocked_on_date;
    };


2.4.3  Foreign

      Tables in a database are often interrelated and some integrity
checking is therefore necessary. Consider the following example.

    record company {
        long   id;
        char   name[21]

        primary key id;
    }

    record product {
        long   company;
        char   name[21];
        
        primary key product_key { company, name };
        foreign key company references company
          on update restrict
          on delete restrict;
    }

      There is a one-to-many relationship between the company and the
product table. A company can have many products, but a product can only be
manufactured by one company. The compound primary key allows us to find all
products manufactured by a certain company. The foreign key itself ensures
that a product cannot be created for a nonexisting company. Conversely, the
delete clause ensures that a company which has products cannot be deleted.

      The update clause ensures that the name in a company record cannot
change if that company has products, because that would invalidate the
dependent record's foreign key. The cascade rule is not supported.

      A foreign key can also be null which is often useful. For example in a
self-referencing table. The following table implements a category tree, where
each category is a subcategory of another category. This doesn't work for the
root record, so we set the parent to null. If has_parent is null, the target
of the parent key is not being checked for.

    record category {
        long    id;
        long    parent;
        char    has_parent;
        char    name[21];

        primary key id;
        foreign key parent references category null by has_parent;
    }


2.5 Sorting map

      Countries other than the English speaking often have special
characters in their alphabet that are not present in the ASCII character set.
These might be characters in the latin part of the ISO-8859 character set.
However, the ordinal values of these character do not reflect their alphabetic
ordering. This problem can be solved by a character map. By default, all ASCII
characters have been set to case-insensitive (lower case).

map_decl    -> "map" '{' map { map } '}'

map         -> map_id "->" map_id ';'

map_id      -> "'" char "'"
            |  number

      The character left to the arrow is the character to translate and the
value to the right is the value is should be compared as. It is possible to
map several characters onto the same value. The following map makes the ASCII
characters case sensitive again.

    map {
        'A' -> 'A';
        'B' -> 'B';
        'C' -> 'C';
        'D' -> 'D';
        'E' -> 'E';
        'F' -> 'G';

        ...

        'Z' -> 'Z';
         
    }


3 TOOLS

Typhoon has four tools. dllp which is used to process DDL files. dbdview which
displays DBD files. tyexport which exports a database and tyimport which
imports a database.


3.1 Data Definition Language Processor

      The DDL Processor is used to compile DDL files. ddlp will report
syntactic and semantic errors as they are encountered. It has the following
command line options:

    -a<alignment>
    -f
    -h<header file>

      The -a option specifies the alignment to use. Some architectures, most
notably Intel, allows structure members to be aligned on 1 byte boundaries.
However, on RISC architectures it is a requirement that a value can be read
from memory in a single read operation and thus, cannot cross an word
boundary. In other words, an integer (or float for that mattter) must be
stored on an address that is a multiple of its size.

      The -f option causes ddlp to only generate constants for fields that
are also keys.

      The -h option overrides the default header file name.


3.2 Database Definition Viewer

This tool displays the tables that make up a DBD file.


3.3 Export tool

      The contents of a database can be exported to ASCII files, where each
field is separated by a comma. To export tables, an export specification must
be made. It can be handwritten, but it is easier to let tyexport generate it.
This can be done with the following command:

    tyexport -g <database name>

      The export specification is written to a file with ".exp" extension.
The export specification contains the tables and fields to export. String will
be enclosed in double quotes and characters in single quotes if printable,
otherwise the ordinal value is written. For unions, the control field
determines which union member is written.

      An export specification can also be used to import a database, just
change the "export" keyword first in the file to "import".

NOTE! floats are not supported.


3.4 Import tool

      Data can be imported from ASCII files where the fields are separated
by commas and the strings enclosed in double quotes. Character fields can be
either integers or characters enclosed in single quotes. Otherwise the format
of the import specification is the same as the one for tyexport.

NOTE! floats are not supported.



4 APPLICATION PROGRAMMING INTERFACE

      This section describes the Application Programming Interface (API)
that is used by the programmer to modify and search a database.


4.1 Currency concept

      Typhoon uses the concept of 'currency' in the sense that it has a
current database, a current record and a current position in each index. For
instance, after opening a database that database becomes the current database.
All subsequence operations are performed on that database until it is closed
or another database is opened.

      The same principle applies to records. After creation or lookup of a
record that record becomes the current record. Subsequence read, write or
delete operations are performed on that record.


4.2 Status codes

      The API functions all return a status code that indicates the status
of the operation. db_status contains the status code until the next API
function call.

      db_subcode is used to hold elaborated status information not provided
by db_status. For example, if db_status returns S_DUPLICATE, the conflicting
field or key ID in stored in db_subcode.


NOTE! In a multi-threaded program, special care must be taken that two
      threads do not destroy each other's db_status and db_subcode value.


The following constants indicate the status of an operation.

S_OKAYThe operation was successful.

S_NOTFOUND
             The function called was not able to find the desired object.
             This value can be returned by all key and record navigation
             functions.

S_DUPLICATE
             The operation would cause duplicate keys in a unique index. The
             conflicting field or key id is stored in db_subcode.

S_NOMEM      The operation could not complete because of insufficient
             memory.

S_NOTAVAIL
             The database is not available because another process has
             opened it in exclusive mode.

S_IOFATAL    A fatal I/O operation occurred.

S_FOREIGN    A record could not be created or updated because the target key
             in a parent table did not exist. The ID of the target table is
             stored in db_subcode.

S_RESTRICT
             The primary key in the object record has changed, and would
             cause dependent tables to contain records with invalid
             references. The ID of the dependent table causing the problem
             is stored in db_subcode.

The following constants are returned due to a programming error. For example,
a record id was specified where a key id was expected. These errors should
therefore not be returned when an application has been debugged.

S_NOCRThere is no current record. For example, returned by d_recread() after
      a d_keyfind() that returned S_NOTFOUND.

S_NOCDThere is no current database.

S_INVDB      Invalid database specified.

S_INVREC     Invalid record ID specified.

S_INVFLD     Invalid field ID specified.

S_NOTKEY     The specified ID is not a key ID.

S_RECSIZE    The size determinator of a variable length field contains a
             value greater than the maximum size.

S_BADTYPE    Some parameter contained a bad type.

S_BADPARM
             Some parameter had an invalid value.



4.3 Opening and closing

4.3.1 d_open

      Open a database

SYNOPSIS
      #include <typhoon.h>

      d_open(char *dbname, char *mode)

DESCRIPTION
      d_open opens a database in either shared or exclusive mode. If the
      database does not already exist it is created without warning. The
      dbd-file must be placed in the path specified by d_dbdpath(1) and the
      database files must be placed in the path specified by d_dbfpath(1).
      dbname is the name of the dbd-file without extension. mode is "s" or
      "x" for shared and exclusive mode, respectively.

      If d_open returns S_OKAY the database becomes the current database.

      If the database has already been opened by another process in
      exclusive mode, d_open returns S_UNAVAIL.

DIAGNOSTICS
      The status code returned by the function is also stored in the global
      variable db_status.

      S_OKAY Database opened successfully.

      S_NOMEM      Out of memory.

      S_INVDB      Invalid database name.

      S_FATALIO    Fatal file i/o error.

      S_UNAVAIL    The database has been opened in exclusive mode by
                   another process.

CURRENCY CHANGES
      The opened database becomes the current database.


SEE ALSO
      d_dbdpath(1), d_dbfpath(1), d_setfiles(1), d_close(1), d_destroy(1).


4.3.2 d_close

      Closes a database

SYNOPSIS
      #include <typhoon.h>

      d_close()

DESCRIPTION
      d_close closes the current database. If no database is currently open,
      S_NOCD is returned.

DIAGNOSTICS
      The status code returned by the function is also stored in the global
      variable db_status.

      S_OKAY Database closed successfully.

      S_NOCD No current database.

CURRENCY CHANGES
      There is no current database.

SEE ALSO
      d_open(1)



4.3.3 d_dbget

      Get the id of the current database.

SYNOPSIS
      #include <typhoon.h>

      d_crget()

DESCRIPTION
      When a database is opened it is assigned an internal id which it
      retains until it is closed. d_dbget gets the id of the current
      database. This id can be used in d_dbset(1) to make another open
      database the current database.

DIAGNOSTICS
      The status code returned by the function is also stored in the global
      variable db_status.

      S_OKAY Operation successful.

      S_NOCD There is no current database.

CURRENCY CHANGES
      None.

EXAMPLE
      #include <typhoon.h>

      int cust_dbid;

      d_open("customer", "s");
      d_dbget(&cust_dbid);
      ...
      d_open(....);
      ...
      /* Make the customer database the current database */
      d_dbset(cust_dbid);


SEE ALSO
      d_dbset(1)




4.3.4 d_dbset


      Set the current database

SYNOPSIS
      #include <typhoon.h>

      d_dbset(int dbid)

DESCRIPTION
      Sets the database with id dbid to the current database.

DIAGNOSTICS
      The status code returned by the function is also stored in the global
      variable db_status.

      S_OKAY Operation successful.

      S_INVPARM    There database id specified is invalid.

CURRENCY CHANGES
      None.

EXAMPLE
      See d_dbget(1).


SEE ALSO
      d_dbget(1)



4.3.5 d_dbdpath

      Set the path of the dbd-files.

SYNOPSIS
      #include <typhoon.h>

      d_dbdpath(char *path)

DESCRIPTION
      d_dbdpath sets the path of the database definition files. This
      function should be called prior to calling d_open(1). path can be
      either a relative or an absolute path name. The validity of path is
      not checked until d_open(1) is called.

DIAGNOSTICS
      The status code returned by the function is also stored in the global
      variable db_status.

      S_OKAY This value is always returned.

CURRENCY CHANGES
      None.


SEE ALSO
      d_dbfpath(1), d_open(1).



4.3.6 d_dbfpath

      Set the path of the database files.

SYNOPSIS
      #include <typhoon.h>

      d_dbfpath(char *path)

DESCRIPTION
      d_dbdpath sets the path of the database files. This function should be
      called prior to calling d_open(1) as it determines where database
      files are stored. path can be either a relative or an absolute path
      name. The validity of path is not checked until d_open(1) is called.

      Setting the dbd-path does not affect previously opened databases.

DIAGNOSTICS
      The status code returned by the function is also stored in the global
      variable db_status.

      S_OKAY This value is always returned.

CURRENCY CHANGES
      None.


SEE ALSO
      d_dbdpath(1), d_open(1).



4.3.7 d_setfiles

      Set the maximum number of open files

SYNOPSIS
      #include <typhoon.h>

      d_setfiles(int maxfiles)

DESCRIPTION
      d_setfiles sets the maximum number of files that typhoon may have open
      at the same time. If the current number of open files exceeds
      maxfiles, Typhoon closes the least recently used files.

DIAGNOSTICS
      The status code returned by the function is also stored in the global
      variable db_status.

      S_OKAY Operation successful.

      S_INVPARM    The parameter is invalid.

CURRENCY CHANGES
      None.


SEE ALSO
      d_open(1)




4.4 Record operations


4.4.1 d_fillnew

      Insert a new record.

SYNOPSIS
      #include <typhoon.h>

      d_fillnew(ulong recid, void *buf)

DESCRIPTION
      d_fillnew inserts a new record in a table. recid specifies the type of
      the record stored in buf. The inserted record retains the same
      database address throughout its life in the database.

DIAGNOSTICS
      The status code returned by the function is also stored in the global
      variable db_status.

      S_OKAY The record was successfully inserted.

      S_NOCD There is no current database.

      S_INVREC     The record id is not valid.

      S_DUPLICATE
                   One of the keys in the record would cause duplicates in
                   a unique index. db_subcode contains the id of the
                   conflicting field or key.

      S_RECSIZE    A length determinator of a variable length field
                   contained a illegal value. db_subcode contains the id
                   of the conflicting field.

      S_FOREIGN    The target of a foreign key could not be found.
                   db_subcode contains the id of the target table.

CURRENCY CHANGES
      If d_fillnew returned S_OKAY the record becomes the current record.

EXAMPLE
      #include <typhoon.h>

      struct customer cust;
      strcpy(cust.name, "Pedersen");
      cust.account = 10002;
      if( d_fillnew(CUSTOMER, &cust) != S_OKAY )
          /* handle error */


SEE ALSO
      d_recwrite




4.4.2 d_recwrite

      Update the current record.

SYNOPSIS
      #include <typhoon.h>

      d_recwrite(void *buf)

DESCRIPTION
      d_recwrite updates the contents of the current record.

DIAGNOSTICS
      The status code returned by the function is also stored in the global
      variable db_status.

      S_OKAY The record was successfully updated.

      S_NOCD There is no current database.

      S_NOCR There is no current record.

      S_INVREC     The record id is not valid.

      S_DUPLICATE
                   One of the keys in the record would cause duplicates in
                   a unique index. db_subcode contains the id of the
                   conflicting field or key.

      S_RECSIZE    A length determinator of a variable length field
                   contained a illegal value. db_subcode contains the id
                   of the conflicting field.

      S_FOREIGN    The target of a foreign key could not be found.
                   db_subcode contains the id of the target table.


CURRENCY CHANGES
      None.

EXAMPLE
      #include <typhoon.h>

      struct customer cust;
      strcpy(cust.name, "Pedersen");
      if( d_keyfind(CUSTOMER_PURPOSE, &cust.name) == S_OKAY )
      {
           d_recread(&cust);
           cust.account = 2000;
          if( d_recwrite(&cust) != S_OKAY )
              /* handle error */
      }
SEE ALSO
      d_fillnew(1), d_recread(1)



4.4.3 d_recread

      Read the contents of the current record.

SYNOPSIS
      #include <typhoon.h>

      d_recread(void *buf)

DESCRIPTION
      d_recread reads the contents of the current record into buf. If there
      is no current record, S_NOCR is returned.

DIAGNOSTICS
      The status code returned by the function is also stored in the global
      variable db_status.

      S_OKAY The record was successfully read.

      S_NOCD There is no current database.

      S_NOCR There is no current record.

      S_INVREC     The record id is not valid.


CURRENCY CHANGES
      None.

EXAMPLE
      #include <typhoon.h>

      struct customer cust;
      strcpy(cust.name, "Pedersen");
      if( d_keyfind(CUSTOMER_PURPOSE, &cust.name) == S_OKAY )
      {
          d_recread(&cust);
          printf("account number is %lu\n", cust.account);
      }


SEE ALSO
      d_recwrite(1)


4.4.4 d_delete

      Delete the current record.

SYNOPSIS
      #include <typhoon.h>

      d_delete()

DESCRIPTION
      d_delete removes the current record from its table. If there is no
      current record, S_NOCR is returned. If the record has references, i.e.
      records with foreign keys that reference this record, S_RESTRICT is
      returned.

DIAGNOSTICS
      The status code returned by the function is also stored in the global
      variable db_status.

      S_OKAY The record was successfully deleted.

      S_NOCD There is no current database.

      S_NOCR There is no current record.

      S_RESTRICT   One or more records currently reference this record and
                   the record cannot be deleted.

CURRENCY CHANGES
      None.

EXAMPLE
      #include <typhoon.h>

      if( d_keyfind(CUSTOMER_NAME, "Pedersen") == S_OKAY )
      {
          if( d_delete() != S_OKAY )
          /* handle error */
      }



SEE ALSO
      d_fillnew(1)


4.4.5 d_recfrst

      Find the first record in a table.

SYNOPSIS
      #include <typhoon.h>

      d_recfrst(ulong recid)

DESCRIPTION
      d_recfrst finds the first record in the table specified by recid. If
      d_recfrst returns S_NOTFOUND the table is empty.

DIAGNOSTICS
      The status code returned by the function is also stored in the global
      variable db_status.

      S_OKAY A record was found.

      S_NOTFOUND
                   The key value was not found, i.e. the index is empty.

      S_NOCD There is no current database.

      S_INVREC     The id is not a record id.

CURRENCY CHANGES
      If S_OKAY is returned, the record found becomes the current record.

EXAMPLE
      /* Traverse the customers in randomorder */

      #include <typhoon.h>

      d_recfrst(CUSTOMER);

      while( db_status == S_OKAY )
      {
          struct customer cust;

          d_recread(&cust);
          printf("%s\n", cust.name);
          d_recnext(CUSTOMER);
      }

SEE ALSO
      d_reclast(1), d_recnext(1), d_recprev(1), d_recread(1).


4.4.6 d_reclast

      Find the last record in a table.

SYNOPSIS
      #include <typhoon.h>

      d_reclast(ulong recid)

DESCRIPTION
      d_reclast finds the last record in the table specified by recid. If
      d_reclast returns S_NOTFOUND the table is empty.

DIAGNOSTICS
      The status code returned by the function is also stored in the global
      variable db_status.

      S_OKAY A record was found.

      S_NOTFOUND
                   The key value was not found, i.e. the index is empty.

      S_NOCD There is no current database.

      S_INVREC     The id is not a record id.

CURRENCY CHANGES
      If S_OKAY is returned, the record found becomes the current record.


SEE ALSO
      d_recprev(1), d_recnext(1), d_recprev(1), d_recread(1).


4.4.7 d_recnext

      Find the next record in a table

SYNOPSIS
      #include <typhoon.h>

      d_recnext(ulong recid)

DESCRIPTION
      d_recnext finds the next record in the table specified by recid. If
      d_recnext returns S_NOTFOUND the table is empty.

DIAGNOSTICS
      The status code returned by the function is also stored in the global
      variable db_status.

      S_OKAY A record was found.

      S_NOTFOUND
                   The key value was not found, i.e. the index is empty.

      S_NOCD There is no current database.

      S_INVREC     The id is not a record id.

CURRENCY CHANGES
      If S_OKAY is returned, the record found becomes the current record.

EXAMPLE
      /* Traverse the customers in random order */

      #include <typhoon.h>

      d_recfrst(CUSTOMER);

      while( db_status == S_OKAY )
      {
          struct customer cust;

          d_recread(&cust);
          printf("%s\n", cust.name);
          d_recnext(CUSTOMER);
      }

SEE ALSO
      d_recfrst(1), d_reclast(1), d_recprev(1), d_recread(1).


4.4.8 d_recprev

      Find the previous record in a table.

SYNOPSIS
      #include <typhoon.h>

      d_recprev(ulong recid)

DESCRIPTION
      d_recprev finds the previous record in the table specified by recid.
      If d_recprev returns S_NOTFOUND the table is empty.

DIAGNOSTICS
      The status code returned by the function is also stored in the global
      variable db_status.

      S_OKAY A record was found.

      S_NOTFOUND
                   The key value was not found, i.e. the index is empty.

      S_NOCD There is no current database.

      S_INVREC     The id is not a record id.

CURRENCY CHANGES
      If S_OKAY is returned, the record found becomes the current record.


SEE ALSO
      d_recfrst(1), d_reclast(1), d_recnext(1), d_recread(1).


4.4.9 d_crget

      Get the database address of the current record.

SYNOPSIS
      #include <typhoon.h>

      d_crget()

DESCRIPTION
      d_crget gets the database address of the current record. If no
      database is currently open, S_NOCD is returned.

DIAGNOSTICS
      The status code returned by the function is also stored in the global
      variable db_status.

      S_OKAY Operation successful.

      S_NOCD There is no current database.

      S_NOCR There is no current record.

CURRENCY CHANGES
      None.


SEE ALSO
      d_crset(1)


4.4.10 d_crread

      Read a field from the current record.

SYNOPSIS
      #include <typhoon.h>

      d_crread(ulong fieldid, void *buf)

DESCRIPTION
      d_crread copies the contents of the field specified by fieldid into
      the buffer buf. If the field is a variable length field, only the
      actual number of bytes in the field is copied.

DIAGNOSTICS
      The status code returned by the function is also stored in the global
      variable db_status.

      S_OKAY Operation successful.

      S_NOCD There is no current database.

      S_NOCR There is no current record.

      S_INVFLD     The id is not a valid field.

CURRENCY CHANGES
      None.

EXAMPLE
      /* Get Pedersen's account number */

      #include <typhoon.h>

      if( d_keyfind(CUSTOMER_PURPOSE, "Pedersen") == S_OKAY )
      {
          unsigned long account;

          d_crread(CUSTOMER_ACCOUNT, &account);
          printf("Account number %lu\n", account);
      }


SEE ALSO
      d_recread(1), d_keyread(1)


4.5 Key operations


4.5.1 d_keyfind

      Search an index for a specific key value.

SYNOPSIS
      #include <typhoon.h>

      d_keyfind(ulong keyid, void *buf)

DESCRIPTION
      d_keyfind is used to lookup a record in a table via one of its
      indexes. keyid specifies which index to search and buf contains the
      value to search for. If the index contains more than one occurrence of
      the key value (only possible for non-unique indexes) d_keyfind returns
      the first one.

      The id can be either the id of a compound key or a field that is a key
      by itself.

      If the key value was not found, d_keyfind returns S_NOTFOUND. A
      subsequent call to d_keynext(1) returns next value in the sorting
      order.

      The actual record is not read from the database until d_recread(1) is
      called.

DIAGNOSTICS
      The status code returned by the function is also stored in the global
      variable db_status.

      S_OKAY The key value was found.

      S_NOTFOUND
                   The key value was not found.

      S_NOCD There is no current database.

      S_INVFLD     The id is not a valid field.

      S_NOTKEY     The field id is not a key itself.

CURRENCY CHANGES
      If S_OKAY is returned. the record found becomes the current record.

EXAMPLE
      /* Find the customer called 'Pedersen' */

      #include <typhoon.h>

      if( d_keyfind(CUSTOMER_NAME, "Pedersen") == S_OKAY )
      {
          struct customer cust;
          d_recread(&cust);
          printf("Account number %lu\n", cust.account);
      }


SEE ALSO
      d_keynext(1), d_keyprev(1), d_keyfrst(1), d_keylast(1), d_recread(1).



4.5.2 d_keyfrst

      Find the first key value in an index.

SYNOPSIS
      #include <typhoon.h>

      d_keyfrst(ulong keyid)

DESCRIPTION
      d_keyfrst finds the first key value in the index specified by keyid.
      If d_keyfrst returns S_NOTFOUND the index is empty.

      The id can be either the id of a compound key or a field that is a key
      by itself.

      The actual record is not read from the database until d_recread(1) is
      called.

DIAGNOSTICS
      The status code returned by the function is also stored in the global
      variable db_status.

      S_OKAY The key value was found.

      S_NOTFOUND
                   The key value was not found, i.e. the index is empty.

      S_NOCD There is no current database.

      S_INVFLD     The id is not a valid field.

      S_NOTKEY     The field id is not a key itself.

CURRENCY CHANGES
      If S_OKAY is returned, the record found becomes the current record.

EXAMPLE
      /* Traverse the customers in alphabetical, ascending
       * order
       */

      #include <typhoon.h>

      d_keyfrst(CUSTOMER_NAME);

      while( db_status == S_OKAY )
      {
          struct customer cust;

          d_recread(&cust);
          printf("%s\n", cust.name);
          d_keynext(CUSTOMER_NAME);
      }

SEE ALSO
      d_keynext(1), d_keyfind(1), d_keyprev(1), d_keylast(1), d_recread(1).



4.5.3 d_keylast

      Find the last key value in an index.

SYNOPSIS
      #include <typhoon.h>

      d_keylast(ulong keyid)

DESCRIPTION
      d_keyfrst finds the last key value in the index specified by keyid. If
      d_keyfrst returns S_NOTFOUND the index is empty.

      The id can be either the id of a compound key, or a field that is a
      key by itself.

      The actual record is not read from the database until d_recread(1) is
      called.

DIAGNOSTICS
      The status code returned by the function is also stored in the global
      variable db_status.

      S_OKAY The key value was found.

      S_NOTFOUND   The key value was not found, i.e. the index is empty.

      S_NOCD There is no current database.

      S_INVFLD     The id is not a valid field.

      S_NOTKEY     The field id is not a key itself.

CURRENCY CHANGES
      If S_OKAY is returned, the record found becomes the current record.

EXAMPLE
      /* Traverse the customers in alphabetical, descending
       * order.
       */

      #include <typhoon.h>

      d_keylast(CUSTOMER_NAME);

      while( db_status == S_OKAY )
      {
           struct customer cust;
      
           d_recread(&cust);
           printf("%s\n", cust.name);
           d_keyprev(CUSTOMER_NAME);
      }

SEE ALSO
      d_keynext(1), d_keyfind(1), d_keyprev(1), d_keylast(1), d_recread(1).



4.5.4 d_keynest

      Find the next key value in an index

SYNOPSIS
      #include <typhoon.h>

      d_keynext(ulong keyid)

DESCRIPTION
      d_keynext finds the next key value greater than or equal to the
      current key value in the index specified by keyid. If d_keynext
      returns S_NOTFOUND the end of the index has been passed. A subsequent
      call to d_keynext will return the first key value in the index.

      The id can be either the id of a compound key or a field that is a key
      by itself.

      The actual record is not read from the database until d_recread(1) is
      called.

DIAGNOSTICS
      The status code returned by the function is also stored in the global
      variable db_status.

      S_OKAY The key value was found.

      S_NOTFOUND   The key value was not found, i.e. the index is empty.

      S_NOCD There is no current database.

      S_INVFLD     The id is not a valid field.

      S_NOTKEY     The field id is not a key itself.

CURRENCY CHANGES
      If S_OKAY is returned, the record found becomes the current record.

EXAMPLE 
      /* Traverse the customers in alphabetical order */

      #include <typhoon.h>

      d_keyfrst(CUSTOMER_NAME);

      while( db_status == S_OKAY )
      {
          struct customer cust;
          d_recread(&cust);
          printf("%s\n", cust.name);
          d_keynext(CUSTOMER_NAME);
      }

SEE ALSO
      d_keyfind(1), d_keyprev(1), d_keyfrst(1), d_keylast(1), d_recread(1).



4.5.5 d_keyprev

      Find the previous key value in an index.

SYNOPSIS
      #include <typhoon.h>

      d_keyprev(ulong keyid)

DESCRIPTION
      d_keyprev finds the next key value smaller than or equal to the
      current key value in the index specified by keyid. If d_keyprev
      returns S_NOTFOUND the start of the index has been passed. A
      subsequent call to d_keyprev will return the last key value in the
      index.

      The id can be either the id of a compound key or a field that is a key
      by itself.

      The actual record is not read from the database until d_recread(1) is
      called.

EXAMPLE
      /* Traverse the customers in alphabetical, descending
       * order.
       */

      #include <typhoon.h>

      d_keylast(CUSTOMER_NAME);

      while( db_status == S_OKAY )
      {
          struct customer cust;

          d_recread(&cust);
          printf("%s\n", cust.name);
          d_keyprev(CUSTOMER_NAME);
      }

DIAGNOSTICS
      The status code returned by the function is also stored in the global
      variable db_status.


      S_OKAY The key value was found.

      S_NOTFOUND
                   The key value was not found, i.e. the index is empty.

      S_NOCD There is no current database.

      S_INVFLD     The id is not a valid field.

      S_NOTKEY     The field id is not a key itself.

CURRENCY CHANGES
      If S_OKAY is returned, the record found becomes the current record.


SEE ALSO
      d_keyfind(1), d_keynext(1), d_keylast(1), d_recread(1).


4.5.6 d_keyread

      Read the most recently found key.

SYNOPSIS
      #include <typhoon.h>

      d_keyread(void *buf)

DESCRIPTION
      d_keyread copies the contents of the most recently accessed key field
      specified by fieldid into the buffer buf. This can be used to
      determine the contents of a key's fields without actually reading the
      record.

DIAGNOSTICS
      The status code returned by the function is also stored in the global
      variable db_status.

      S_OKAY Operation successful.

      S_NOCD There is no current database.

      S_NOCR There is no current record.

CURRENCY CHANGES
      None.

EXAMPLE
      /* Find the smallest customer name */

      #include <typhoon.h>

      if( d_keyfrst(CUSTOMER_PURPOSE) == S_OKAY )
      {
          char name[30];
          d_keyread(name);
          printf("Name %s\n", name);
      }

SEE ALSO
      d_recread(1), d_crread(1)

