












                                      CDB


                         Copyright (C) 1991 by Daytris
                               All rights reserved



                             Revised: May 20th, 1991


INTRODUCTION


Overview

        CDB is a sophisticated database toolkit for MS-DOS and UNIX
        developers.  CDB includes the following features:

            - Quick data access through a sophisticated multi-key ISAM
              implementation.

            - Multiple data models.  Both relational and network data
              models are implemented in CDB.  The network model gives the
              developer the ability to create relationships between
              records without storing unique keys in those records.

            - Data Definition Language (DDL) for defining database layouts.
              The DDL is compiled into a binary format which is used by the
              database server as a roadmap.  Using this concept, a
              developer can define and re-define a database with minimal
              effort and absolutely no code changes.  The DDL is patterned
              after C for ease of programming.

            - Over 30 predefined database function calls for complete
              control of the database.

            - Portability.  CDB is written entirely in C for portability
              and source code is included with your purchase.  A version
              of CDB is also available for the MS-Windows platform.
              Contact the developers for more information about this
              product.

            - C++ compatibility.  The library is callable from both C and
              C++.

            - Automatic re-use of deleted database space.  There is no need
              for database reorganization.  Deleted space is automatically
              reused by CDB.

            - Low overhead.  The CDB database engine requires an extremely
              small amount of memory to operate.

            - Royalty-free distribution rights.  Whether you have one
              customer or thousands, you pay for CDB just once for each
              environment that you are using.

 
Obtaining CDB

        Refer to the document included in this release, ORDER.TXT, to order
        CDB.  Your purchase will include CDB libraries, utilities, royalty-
        free use of library functions, full library and utility source code
        and make files.  A printed manual will also be included.


Future Enhancements

        Listed below are some of the enhancements planned for CDB.  Any
        suggestions would also be greatly appreciated.

        - Multi-user database access.  Networks and/or protocols to be
          supported are currently undefined.

        - Performance enhancements.


Contacting the Developers

        CDB was developed solely by Daytris.  If you have a question about
        the product or any suggestions please contact us at the phone
        number listed below.  Or if you prefer, you can send us electronic
        mail on any of the information services listed.

        Daytris
        81 Bright Street, Suite 1E
        Jersey City, NJ  07302
        201-200-0018

        CompuServe:	72500,1426
        BIX:	        daytris
        GEnie:	        t.fearn


GETTING STARTED 


Unpacking

        CDB is distributed in self-extracting ZIP files.  When you first
        unpack your software, you may want to verify that you have a
        complete set.  If you have the Test Drive version, you should have
        the files included in CDB.EXE.  If you have purchased CDB, you
        should have the files included in both CDB.EXE and CDBSRC.EXE.
        The contents of each are listed below.

        CDB.EXE

                File Name	Description
                ---------       -----------

                CDB.TXT	        This document.
                ORDER.TXT	Order form for CDB.
                REGISTER.TXT	Registration form for CDB.
                README.TXT	Latest CDB info.
                UPDATES.TXT	History of updates made to CDB.
                MSCCDBL.LIB	CDB library.  Microsoft C large model.
                TCCDBL.LIB	CDB library.  Turbo C large model.
                DBDLIST.EXE	Database Definition (DBD) file display
                                utility.
                DDLP.EXE	Database Definition Language Parser.
                TEST.C  	TEST do nothing program.
                TEST.DDL	TEST Data Definition Language File.
                DBMGR.H         CDB header file.
                MSCTEST.MAK	Make file for TEST (Microsoft C).
                                Microsoft NMAKE and UNIX make compatible.
                TCTEST.MAK	Make file for TEST (Turbo C).  Microsoft
                                NMAKE and UNIX make compatible.


        CDBSRC.EXE

                File Name	Description
                ---------       -----------

                LICENSE.TXT	Daytris software license agreement.
                MSCLIB.MAK	Library make file (Microsoft C).
                                Microsoft NMAKE and UNIX make compatible.
                TCLIB.MAK	Library make file (Turbo C).  Microsoft
                                NMAKE and UNIX make compatible.
                DBADD.C	        Library source file.
                DBUPD.C	        .
                DBDEL.C	        .
                DBFIND.C	.
                DBGET.C	        .
                DBCURR.C	.
                DBFILE.C	.
                DBMGR.C	        .
                DBPAGE.C	.
                DBSLOT.C	.
                DBTALK.C	.
                DBFUNCS.C	.
                DBMGR.H	        CDB header file.
                DBXTRN.H	External definitions header file.
                STDINC.H	Header file that includes other header
                                files used in CDB.
                DDLP.MAK	Make file for DDLP.  Microsoft NMAKE and
                                UNIX make compatible.
                MAIN.C	        DDLP source file.
                DDLP.C	        .
                PARSE.C	        .
                ERROR.C	        .
                DDLP.H	        DDLP header file.
                DBDLIST.MAK	Make file for DBDLIST.  Microsoft NMAKE and
                                UNIX make compatible.
                DBDLIST.C	DBDLIST source file.


THE NETWORK DATABASE MODEL


Introduction

        CDB provides both relational and network model features.  The use
        of both models in a data design can greatly increase the
        performance of your database.  For those of you who are already
        familiar with the relational database concepts, you will find the
        network model implementation very refreshing.  For those of you
        who aren't familiar with the relational model, a very brief
        description of relational concepts follows.


The Relational Model

        In a relational database, data is stored in a series of tables.
        Each table consists of a number of columns, which identify a
        particular type of data, and rows, which correspond to a particular
        record in the table.

        Individual records can be retrieved using the key fields defined
        for the table.  If the developer has the desire to make an
        association between two tables, unique key fields must be defined
        in both records and unique data must be stored for retrieval to 
        take place.


What is the Network Model?

        This model allows you to define relationships between records
        through constructs called sets.  A set defines a one-to-many
        relationship between two tables.

        In a relational model, records can only be related (connected)
        by storing unique keys in both tables.  This method creates
        additional unwanted overhead.  Duplicate data is stored in both
        records and duplicate indexes must be managed.

        Using the network model, records are connected by directly storing
        data pointers inside the record.  Where the relational model
        requires multiple disk accesses to locate a related record, the
        network model allows the record to be located in a single disk
        access.  Disk space is also saved when sets are used because no
        index is required.

        Another advantage of using the network model is the flexibility
        of owner/member relationships.  A record may own multiple record
        types.  A record may also be owned by multiple owner records.  An
        example of this would be a 'client' record owning 'invoice'
        records and also owning 'address' records.  In turn, an 'invoice'
        could also own 'address' records, perhaps a shipping and billing
        address.  This kind of flexibility gives the developer the power
        to define complex data relationships with relative ease.


THE DATA DEFINITION LANGUAGE

Introduction

        The Data Definition Language is used for defining a database model.
        The language is basically a superset of C structure definitions.
        If you are familiar with defining C structures, the DDL should be
        very easy to pick up on.

        The DbOpen function call loads a binary image of a DDL file.  The
        binary image is created by compiling the DDL file into a DBD
        (Database Definition) format.  A DDL compiler is included with this
        release, DDLP.EXE (Data Definition Language Parser).


An Example:

        /* sampledb.ddl */

        prefix ABC;

        struct client
            {
            connect	address key street;
            key long	clientnbr;
            key char	name[31];
            char	description[61];
            double	balance;
            };

        struct address
            {
            char	street[31];
            char	city[21];
            char	sate[3];
            key char	zip[11];
            key char	telephone[13];
            char	fax[13];
            };

        struct setup
            {
            key long	nextclientnbr;
            };


        Notice the close resemblence to C structure definitions.  The only
        differences are the prefix, connect, and key words.



        prefix

        The prefix is used internally by the database library when a new
        database file must be created.  In the SAMPLEDB.DDL shown above,
        the prefix is "ABC".  By defining the prefix as "ABC", we are
        telling the library to use "ABC" as the first 3 characters of any
        file that is created for the SAMPLEDB database.

        The prefix can be from 1 to 4 characters in length.  If a prefix is
        not defined, a default prefix, "TEST", is used.

        For more information about the CDB database file naming
        conventions, refer to the 'Database File Names' section in this
        manual.


        connect

        When using the connect keyword, you are taking advantage of the
        network database model implementation of CDB.  Network model
        concepts can greatly increase the performance and efficiency of
        your database.

        The connect keyword defines a relationship between two records.

        struct client
            {
            connect	address key street;
                .
                .
            };

        In this example, we are defining a relationship between the client
        record and the address record.  The client record will be an owner
        of the address record.  The address record is a member of the
        client record.  For now, ignore the 'key street' part of the
        connect phrase.

        By declaring this set relationship, we now have the capability to
        make connections between client and address records using the
        DbSet... function calls.  A client may own 0, 1 or many address
        records.  Without the network model concepts that we have just
        shown you, to make connections between two records would require
        the storage of a unique key in each individual record.

        void Function()
            {
            static CLIENT client = {1000L,"Daytris","A software company",
                0.00};
            static ADDRESS address = {"81 Bright Street, Suite 1E",
                "Jersey City","NJ","07302","201-200-0018",""};

            DbRecordAdd( "client", &client);
            DbRecordAdd( "address", &address);
            DbSetAdd( "client", "address");
            }


        The example above shows how to make a set connection between two
        records by using the DbSetAdd function.  After the function call,
        the client "Daytris" is the owner of 1 address record.  This
        address record can be retrieved using the DbSetGetFirst call:

        DbSetGetFirst( "client", "address", &address);


        Now lets add another address record to the set:

        void Function()
            {
            long key = 1000L;
            static ADDRESS address = {"30 Broad Street","New York","NY",
                "10015","212-555-1212","212-555-1212"};

            /* Make client #1000 current */
            DbRecordFindByKey( "client", "clientnbr", &key);

            /* Add another member */
            DbRecordAdd( "address", &address);
            DbSetAdd( "client", "address");
            }

        The client "Daytris" now owns 2 address records.  We can use the
        DbSetGetFirst, DbSetGetLast, DbSetGetNext, or DbSetGetPrev to
        retrieve any of the address records in the set.

        Now lets take a look at how the sets are ordered.  If we make a
        DbSetGetFirst call after adding the sets shown above, which address
        record would be returned?  Lets return to our original DDL example:

        struct client
            {
            connect	address key street;
                .
                .
            };

        Member records can be ordered two ways.  By the order in which they
        are added, or by a field in the member record.  In our example, the
        set order is by the street field in the address record.  Therefore,
        a DbSetGetFirst( "client", "address", ...) call would return the
        "30 Broad Street" address record.  If the connect address phrase
        were defined without a key:

        struct client
            {
            connect	address;
                .
                .
            };


        the address members would be stored in the order that they were
        added.  Therefore, a DbSetGetFirst( "client", "address", ...)
        call would return the "81 Bright Street, Suite 1E" address because
        this address was added first.

        A record can have more than one member.  A record can also be owned
        by more than one owner.  To illustrate this, lets take the
        SAMPLEDB.DDL and expand it to include invoicing capabilities.

        /* sampledb.ddl - with invoicing */

        prefix ABC;

        struct client
            {
            connect	address key street;
            connect	invoice;
            key long	clientnbr;
            key char	name[31];
            char	description[61];
            double	balance;
            };

        struct address
            {
            char	street[31];
            char	city[21];
            char	state[3];
            key char	zip[11];
            key char	telephone[13];
            char	fax[13];
            };

        struct invoice
            {
            connect 	address;
            connect	invoiceline;
            key long	invoicenbr;
            long	date;
            double	totalprice;
            };

        struct invoiceline
            {
            long	quantity;
            char	description[31];
            double	unitprice;
            double	lineprice;
            };

        struct setup
            {
            key long	nextclientnbr;
            long	nextinvoicenbr;
            };


        In this example, a client record can own multiple address records
        and multiple invoice records.  This makes sense because a client
        could have more than one address, i.e. a shipping and billing
        address.  The client could also have more than one invoice if more
        than one order is placed.

        Also in this example, an owner/member relationship exists between
        the invoice and address records.  If our invoice has both 'ship to'
        and 'bill to' addresses, the shipping address could be stored as
        the first member in the set and the billing address could be stored
        as the next member.

        A variable number of line items could exist on an invoice.  This is
        the reason for the invoiceline record and its relationship with the
        invoice.  An invoice record will own its invoice lines.

        The following example illustrates how all data pertaining to a
        specific invoice might be retrieved.  Note: it is suggested that
        the CDB return values be taken more seriously than illustrated
        below.

        typedef struct invoice INVOICE;
        typedef struct invoiceline INVOICELINE
        typedef struct client CLIENT;
        typedef struct address ADDRESS;


        void Function()
            {
            long key = 2000L;
            UINT status;
            INVOICE invoice;
            INVOICELINE invoiceline;
            CLIENT client;
            ADDRESS shipaddress;
            ADDRESS billaddress;

            /* Get invoice #2000 */
            DbRecordGetByKey( "invoice", "invoicenbr", &invoice, &key);

            /* Get the client that owns the invoice */
            DbSetGetOwner( "client", "invoice", &client);

            /* Get the 'ship to' and 'bill to' addresses */
            DbSetGetFirst( "invoice", "address", &shipaddress);
            DbSetGetNext( "invoice", "address", &billaddress);

            /* Retrieve all invoice lines (assuming at least 1 line) */
            status = DbSetGetFirst( "invoice", "invoiceline", &invoiceline);
            while( status != E_NONEXT && status != E_NOTFOUND)
                {
                /* Store the line */

                /* Get the next line */
                status = DbSetGetNext( "invoice", "invoiceline",
                        &invoiceline);
                }
            }

        This example illustrates some of the capabilities that you have
        with set relationships.  The possibilities are endless.


        key

        The key word is used for defining key fields in records and key
        fields to be used in set relationships.  See the connect section
        directly preceding this section for more details about the key
        fields in set relationships.

        Key fields are stored in ascending order in slots on pages in a
        key file.  The key file is made up of a series of linked pages.

        struct client
            {
            connect	address key street;
            key long	clientnbr;
            char	name[31];
            char	description[61];
            double	balance;
            };


        This DDL structure definition contains only one key field,
        "clientnbr".  Therefore all pages in the corresponding key file
        will contain will contain slots of sorted client numbers.

        struct client
            {
            connect	address key street;
            key long	clientnbr;
            key char	name[31];
            char	description[61];
            double	balance;
            };

        The DDL structure definition now contains two key fields,
        "clientnbr" and "name".  Therefore two types of key pages will
        exist in the key file for this record type.  Some pages will
        contain slots of sorted client numbers and other pages will contain 
        slots of sorted client names.  The data stored on the key file
        pages is directly related to the number of key fields defined in
        the DDL file.

        To maximize the efficiency of your database, it is suggested that
        you use as few key fields as possible.  The maximum number of key
        fields allowed in a record is defined as MAXKEY in DBMGR.H.  It is
        currently set to 8.  See 'Modifying the Database Internals' section
        for more information about MAXKEY.

        If a structure is defined without a key field, the only way to
        access a record of this type is with a set relationship.  The
        structure defined without a key field must be a member of another
        record.

        struct client
            {
            connect	address key street;
            key long	clientnbr;
            key char	name[31];
            char	description[61];
            double	balance;
            };

        struct address
            {
            char	street[31];
            char	city[21];
            char	state[3];
            char	zip[11];
            char	telephone[13];
            char	fax[13];
            };

        In this example, the address record contains no key fields.
        Therefore, the address record cannot be accessed using any
        DbRecord... function calls because these functions require a key
        field as a parameter.  However, the address is a member of the
        client record.  Therefore, it could be accessed with the
        DbSetGet... function calls, provided a relationship exists.


DDL Limitations

        The Data Definition Language does not currently support the
        definition of structures or unions defined from within a structure.
        Example:

        struct client
            {
            struct address addr;
            key long	clientnbr;
            key char	name[31];
            char	description[61];
            double	balance;
            };

        These deficiencies will be supported in a later release of CDB.
        A way to get around this problem for now is to allocate enough
        space as a char field for the structure or union that could not be
        included.  Example (assuming the address structure length is 92
        bytes):

        struct client
            {
            char 	addr[92];
            key long	clientnbr;
            key char	name[31];
            char	description[61];
            double	balance;
            };

        After DDLP compilation, modify the C header file output by DDLP to
        include the proper structure definition.


DATABASE CURRENCY


What is Currency?

        Currency refers to the record position in a database key file.  It
        is very similiar to the file pointer in an open file. For example,
        when you first open a file using the C run-time library "open"
        function, the file pointer points to the first byte in the file
        (it could point to the last byte depending on how its opened).
        After the file is open, you can seek to different positions in the
        file and read or write data.  The file pointer position is kept
        internally by the operating system.  You could think of this
        position as the current position or "currency".

        In CDB, the concepts are very similar.  Each record structure
        defined in a DDL will have an associated currency table when this
        database is opened.


An Example:

        /* sampledb.ddl */

        prefix ABC;

        struct client
            {
            connect	address key street;
            key long	clientnbr;
            key char	name[31];
            char	description[61];
            double	balance;
            };

        struct address
            {
            char	street[31];
            char	city[21];
            char	state[3];
            key char	zip[11];
            key char	telephone[13];
            char	fax[13];
            };

        struct setup
            {
            key long	nextclientnbr;
            };


        When this database is opened using DbOpen, 3 currency tables will
        be initialized to zero.  One for each record type: 'client',
        'address', and 'setup'.  The currency table contains the following
        format:

        struct currency_index
            {
            struct
                {
                UINT	page;
                UINT	slot;
                } keydba[MAXKEY];	/* Array of key dba's */
            ULONG	datadba;	/* Data database address */
            };


        keydba

        The currency table consists of two parts; a key currency (keydba)
        and a data record currency (datadba).  A keydba exists for each
        key defined in the record table.  In the example defined above,
        the 'client' record would use the first two keydba structures in
        the currency_index table for key currency storage.  Records that
        do not have any keys defined would not make use of the keydba part
        of the currency_index.

        Lets say that we have three 'client' records in our database.  The
        contents of each are as follows:

        Record 1:	1000L,"Daytris","Software Development",0.00
        Record 2:	1001L,"Microsoft","Software Development",10000.00
        Record 3:	1002L,"CompuServe","Computer Services",100.00

        When the database is opened, the currency_index for the 'client'
        record, as well as all other records, is null.  In other words,
        "the client record does not have currency".  If we were to issue a:

        DbRecordFindNext( "client", "clientnbr");

        at this time, an E_NONEXT return value would result.  There is no
        next record to find!  However, if we were to issue a:

        DbRecordFindFirst( "client", "clientnbr");

        the return value would be 0 indicating a successful call.  After
        this call, the keydba structure within the currency_index for the
        'client' record would contain the appropriate page and slot number
        of the first record for the "clientnbr" index.  In this case, the
        keydba[0] structure within the 'client' currency_index would point
        to client number 1000L, Daytris.

        If we were to now issue a:

        DbRecrordFindNext( "client", "clientnbr");

        the keydba[0] structure within the 'client' currency_index would
        point to the next client sorted by "lClientNbr".  In our example,
        it would point to 1001L, Microsoft.

        Keep in mind that we are not retrieving any records, we are only
        setting currency for the 'client' record type.  If we would want
        to retrieve a record, we would use the DbRecordGet... function
        calls.  Using the DbRecordFind... function calls we can essentially
        "seek" to positions within the database based on any index field
        within a record type.


        datadba

        The datadba field in the currency_index is used for "set" currency.
        When we issue a DbSetFind.. function call, the datadba is used to
        locate the current set record.  The datadba field contains the
        actual slot number of the current record.  "Next" and "previous"
        set pointers are stored at the beginning of each data slot in a
        data file.


Differences Between Find and Get Function Calls

        The DbRecordFind... and DbSetFind... function calls only set
        currency for a specific record type.  They do not retrieve records.
        You may want to think of the Find function calls as performing the
        same task as the C run-time lseek function.  Essentially, we are
        seeking to a position in the database.

        If you wish to retrieve a record, use the DbRecordGet... or the
        DbSetGet... function calls.  Note:  The DbRecordGet... and
        DbSetGet... function calls call their Find counterparts first, and
        then retrieve the current record.  For example, the
        DbRecordGetFirst function will perform a DbRecordFindFirst and then
        a DbRecordGetCurrent function call.


Storing Currency Tables

        You can retrieve a copy of the current currency_index for each
        record defined in the DDL.  Why would you want to do this?

        Lets suppose that you have a database that contains hundreds of
        'client' records.  Your application must be able to display these
        'client' records in a small window, but you don't have enough
        memory to keep all of the 'client' records resident.  Or it may
        be a waste of memory to do so.  This is where storing currency
        tables becomes necessary.  

        As previously explained, each record type defined in a DDL has an
        associated currency table.  The contents of a currency table can
        be retrieved or updated at any time.  Therefore, in the example
        explained above, we could retrieve a window of 'client' records
        along with their associated currency_index tables.  Example:


        UINT GetWindowOfClients( BOOL bFirstTime)
            {
            register short i;
            UINT status;
            struct currency_index currency;

            for( i=0 ; i<WINDOW_LINES ; i++)
                {
                /* Get the record */
                if( bFirstTime)
                    {
                    bFirstTime = FALSE;
                    status = DbRecordGetFirst( "client", "clientnbr",
                        &client);
                    }
                else
                    status = DbRecordGetNext( "client", "clientnbr",
                        &client);
                if( status)
                    return status;

                /* Get the currency table */
                status = DbRecordGetCurrency( "client", &currency);

                /* Put the record in a window and store along with it
                   the associated currency table */
                }
            }

        After calling this routine, we have a window of 'client' records.
        For each 'client' record we also have an associated currency table.
        If the user were to select a specific 'client' in the window, we
        could retrieve this 'client' with the following database calls:

        DbRecordUpdCurrency( "client", &currency);
        DbRecordGetCurrent( "client", "clientnbr", &client);

        The "currency" structure passed in the DbRecordUpdCurrency call
        represents the currency_index of the selected 'client' record.
        Because we have a currency table stored for each 'client' record
        in the window, we can retrieve any record in the window using
        this method.


        Deleting a current record

        Beware when deleting a current record.  If you are storing a
        series of currency tables as we have done in the example explained
        above, deleting a current record will invalidate currency tables
        that followed this record.  Suppose we have a window of 'client'
        records:

        Record 1:	1010L,"ABC Corp.","Diskette Manufacturer",0.00
        Record 2:	1011L,"XYZ  Corp.","Hard Drive Manufacturer",0.00
        Record 3:	1012L,"BYTE Magazine","Software Publication",0.00
        Record 4:	1013L,"Sharp","Electronics",0.00
        Record 5:	1014L,"Collins","Radio Electronics",0.00

        We have also stored currency tables associated with each 'client'
        record in the window.
 
        If for example we delete Record 2, "XYZ Corp.", the currency
        tables associated with Records 3, 4, and 5 will now be invalid.
        Remember that currency tables contain the page and slot of a data
        item.  If a record is deleted, the key fields are removed from
        their associated pages.  The data (slots) on a key page are
        compressed to be contiguous.  Therefore, key fields stored after
        the deleted record will be moved up 1 slot.  Or possibly, if the
        slot is the last slot on a page, the page will be removed entirely.

        To avoid retaining invalid currency tables in memory after deleting
        a record, currency tables should be re-retrieved after the deletion.
        To do this, start the retrieval with the record before the deleted
        record.  In the example above, Record 2 is being deleted.  After
        the deletion, restore the Record 1 currency table (using
        DbRecordUpdCurrency) and re-retrieve next records and corresponding
        currency tables (using DbRecordGetCurrency)  until the window is
        full.


        Updating a current record

        An update, like the delete described above, can create similar
        problems.  This is only a problem if the key field that was used
        for the retrieval of records is updated.  In this case, the slots
        could be rearranged in an order unknown to the calling application.
        The only way to solve this problem, is to re-retrieve the records
        and their associated currency tables from the beginning after the
        update takes place.


DATABASE INTERNALS


Memory Requirements

        CDB requires a very small amount of memory to operate.  The
        library allocates all memory required for data access and
        manipulation during the DbOpen call.  Memory is NOT allocated by
        the database during the execution of any other database call.

        CDB will consume approximately 45K of code and data space.  A
        portion of this space is allocated when a database is opened.  A
        variable amount of memory is also allocated and depends upon the
        size of the DBD file.  This space is used for DBD record, owner,
        member, and field tables.  To calculate the variable amount of 
        memory that will be required by a DBD, use the following formula:

        DBD memory consumption = (number of records * 46) +
            (number of owners * 4) + (number of members * 8) +
            (number of fields * 40)

        The DBDLIST utility included in this release will display the
        number of records, owners, members, and fields in a DBD.  As an
        example, the SAMPLEDB.DBD displayed in this document will consume
        only 590 bytes for the tables described above.


Database Files

        Database records are organized in data and key files.  Each record
        type defined in the DDL will have an associated data file.  If any
        key fields exist in this record, the record type will also have a
        key file.  Key files have a .key extension while data files have a
        .dat extension.


        File Naming

        /* sampledb.ddl */

        prefix ABC;

        struct client
            {
            connect	address key street;
            key long	clientnbr;
            key char	name[31];
            char	description[61];
            double	balance;
            };

        struct address
            {
            char	street[31];
            char	city[21];
            char	state[3];
            key char	zip[11];
            key char	telephone[13];
            char	fax[13];
            };

        struct setup
            {
            key long	nextclientnbr;
            };

        Key and data files are not created until the first record of a
        specific record type is added.  For example, when the first
        'client' record is added to the database, a data and key file will
        be created.  The names used for the .DAT and .KEY file are derived
        as follows:

        sprintf( keyfile, "%s%4.4d", prefix, orderinDBD);
        sprintf( datfile, "%s%4.4d", szPrefix, orderinDBD);

        The prefix is the prefix defined in the DDL.  In our example, the
        prefix is "ABC".  The orderinDBD is the record number in the
        database definition (.DBD) file.  When DDLP compiles the DDL into
        DBD format, record tables are stored describing each record
        structure definition.  The record tables are stored in alphabetical
        order in the DBD file.  In our example, the record order in the DBD
        is 'address', 'client', then 'setup'.  Therefore, when the first
        'client' record is added to the database, ABC0001.DAT and
        ABC0001.KEY are created.  When the first 'address' record is added,
        ABC0000.DAT and ABC0000.KEY are created.

        If a record definiton does not contain a key field, a key file is
        not created for this record.


        Key File Layout

        Key files are organized as a series of pages.  Pages contain a
        series of slots.  The slots contain the key data.  Slots on a page
        are in sorted order.  A key file will contain pages of keys for a
        specific record type.  For example, in SAMPLEDB.DDL shown above,
        the 'client' key file will contain pages of keys for the 'client'
        record.  

        Some pages will contain client numbers and some pages will contain
        client names.  A page will not contain both client numbers and
        client names.

        The key file structures are listed below:

        struct  key_file_index
            {
            CHAR	name[12];	/* Key file name */
            UINT	nextavailpage;	/* Next available page */
            UINT	firstdelpage;	/* First page in the delete */
                			/* chain. */
            UINT	pagenbr[MAXKEY];/* Key 1st page index */
            };

        This structure is included at the beginning of every key file.  It
        contains necessary pointers for finding the next available page,
        first deleted page in the delete chain, and the first page for
        each key field defined in the record.

        struct  key_page_index
            {
            UINT	prevpage;	/* Previous page in sort tree */
            UINT	nextpage;	/* Next page in sort tree */
            UINT	slotsused;	/* Number of slots used on page */
            UINT	slotsize;	/* Size of key slot */
            UINT	flags;	        /* Bit 0 - page is full */
            };

        This structure is included at the beginning of each page in the
        key file.  The slots in the key file consist of nothing but raw
        key field data.


        Data File Layout

        Data files do not contain pages.  They are organized as a series
        of slots in a file.  Pages are not needed here because data files
        contain only record data.  They are indexed by their respective
        key files.

        The data file structures are listed below:

        struct  data_file_index
            {
            CHAR	name[12];	/* Data file name */
            ULONG	nextavailslot;	/* Next available slot */
            ULONG	firstdelslot;	/* First slot in the delete */
                    			/* chain. */
            UINT	slotsize;	/* Size of data slot */
            CHAR	filler[10];
            };

        This structrure is included at the beginning of every data file.
        It contains necessary pointers for finding the next available slot,
        first deleted slot in the delete chain, and the data slot size.

        struct  data_slot_index
            {
            UINT	offsettodata;	/* Offset to actual data */
            ULONG	nextdel;	/* DBA of next member in the */
                    			/* delete chain. */
            };

        This structure is included at the beginning of each data slot.  A
        data slot also contains owner and/or member data if the record type
        is an owner of or member of another record.  The owner and member
        pointer tables are not shown here.  In summary, a data slot
        contains a data_slot_index, owner data tables, member data table,
        followed by the actual data record.


Modifying Internal Definitions

        It is relatively easy to change some of the global definitions
        used CDB.  In some extreme cases, modification may be necessary.
        This, of course, depends on your database model (data definition
        file).  Source code is required to make any of the changes to the
        definitions listed below.

        All definitions described are included in DBMGR.H.


        #define NBRHANDLES  12

        This value is the number of database files that can be open at
        one time.  A data file exists for every record defined in the DDL
        if at least one record of that type has been added.  If the DDL
        structure definition contains one or more key fields, a key file
        will also be created.

        struct client
            {
            key long	lClientNbr;
            key char	szName[31];
            char	szDescription[61];
            double	dBalance;
            };

        In this DDL example, two files will be created when the first
        record of this type is added to the database.  A data file will
        be created and because at least 1 key field exists, a key file
        will also be created.

        CDB uses an LRU (least recently used) algorithm to manage
        database file handles.  If CDB needs to open a file and 12
        database files are already open, CDB closes the least recently
        used handle and proceeds to open the new file.  The new file
        handle is then placed in the LRU table.

        If your database model contains more than 12 database files,
        database perfomance may be enhanced by increasing the NBRHANDLES
        value.  Note: The maximum number of file handles available for a
        single task under DOS is 20.  5 are reserved for internal use.
        If you leave NBRHANDLES defined as 12, only 3 are available for
        your application.


        #define MAXKEY  8

        This value is the maximum number of key fields that a single
        record definition can contain.  Increase this value only if you
        have more than 8 key fields defined in a single record definition.


        #define KEYPAGESIZE  512

        Key fields are stored in sorted order in slots on pages.  A key
        file is made up of a header and a series of these pages.
        KEYPAGESIZE is the size of a key page.  If your database key
        fields are very large, you might increase the performance of the 
        database by increasing this value.  If modified, KEYPAGESIZE
        should be a multiple of the average key field length.  Note:
        The larger the key page size, the longer the access time for
        reads and writes.


        #define NBRPAGES  16

        This value is the number of key pages that are buffered in RAM
        by CDB.  These buffers are managed using an LRU (least recently
        used) algorithm for maximum efficiency.


        #define DATAPAGESIZE  2048

        Data records are stored in slots on pages.  The pages are stored
        in the data file (.DAT).  This value is the size of the data pages.
        It is recommended that this value be a power of 2.


        #define DATASLOTSIZE  1024

        This value is the maximum size of a data slot.  A data slot
        contains a small header, followed by owner tables (if any),
        followed by member tables (if any), followed by the actual data.
        You will need to increase this value if your record sizes, when
        plugged into the formula below, exceed 1024.

        Formula:

        6 +
        (number of member record types this record can own * 8) +
        (number of owner record types that can own this record * 12) +
        C structure length (in bytes)

        If you have very large C structures you should check them.  It is
        recommended that DATASLOTSIZE be a power of 2.


        Example:

        /* sampledb.ddl */

        struct client
            {
            connect	address key street;
            key long	clientnbr;
            key char	name[31];
            char	description[61];
            double	balance;
            };

        struct address
            {
            char	street[31];
            char	city[21];
            char	state[3];
            key char	zip[11];
            key char	telephone[13];
            char	fax[13];
            };

        In this DDL example, the size of a 'client' data slot would be:

        6 +
        (1 * 8) +
        (0 * 12) +
        104 = 118

        The size of an 'address' data slot would be:

        6 +
        (0 * 8) +
        (1 * 12) +
        92 = 110


UTILITIES


DDLP.EXE

        DDLP is the Data Definition Language Parser (compiler).  It reads
        the DDL file and creates a binary database definition file with a
        .DBD extenstion.  DDLP also creates a C header file with a .H
        extension.

        The DBD file name is used with the DbOpen function call.  The
        DbOpen function passes the DBD file name as a parameter.  The DBD
        file is read into memory by CDB and serves as a roadmap for the
        database.

        The maximum size of a .DDL file that DDLP can process is 65535
        bytes.  A complete list of DDLP error messages are provided in the
        'Error Messages' section in this manual.


        Syntax:

        DDLP filename(.ddl)


        Example:

        DDLP sampledb.ddl

        In this example, DDLP will create SAMPLEDB.DBD and SAMPLEDB.H if
        the compilation is successful.


DBDLIST.EXE

        DBDLIST displays the contents of the binary database definition
        file (.DBD) created by DDLP.  A header, record definitions, owner
        definitions, member definitions, and field definitions are
        displayed.

        DBDLIST does not display the contents of any data or key files.


        Syntax:

        DBDLIST filename.dbd


        Example:

        DBDLIST sampledb.dbd


USING THE C-API


Introduction

        The CDB C-API library is callable from both C and C++ modules.
        Over 30 functions are available.  Function prototypes are defined
        in DBMGR.H.


Functions by Category

        Database Management

        DbClose		        Close a database.
        DbFlush		        Flush all data files to disk.
        DbOpen		        Open a database.


        Record Management

        DbRecordAdd		Add a record.
        DbRecordDelete	        Delete a record.
        DbRecordUpdate	        Update a record.


        Record Find

        DbRecordFindByKey	Find a record by key value.
        DbRecordFindFirst	Find the first record.
        DbRecordFindLast	Find the last record.
        DbRecordFindNext	Find the next record.
        DbRecordFindPrev	Find the previous record.


        Record Retrieval

        DbRecordGetByKey	Get a record by key value.
        DbRecordGetCurrent	Get the current record.
        DbRecordGetFirst	Get the first record.
        DbRecordGetLast	        Get the last record.
        DbRecordGetNext	        Get the next record.
        DbRecordGetPrev	        Get the previous record.


        Record Currency

        DbRecordGetCurrency	Get the currency table of a record type.
        DbRecordUpdCurrency	Update the currency table of a record type.


        Set Management

        DbSetAdd		Make a set connection between two records.
        DbSetDelete		Remove a set connection between two records.


        Set Find

        DbSetFindFirst		Find the first member record in an
                                owner/member relationship.
        DbSetFindLast		Find the last member record in an
                                owner/member relationship.
        DbSetFindNext		Find the next member record in an
                                owner/member relationship.
        DbSetFindPrev		Find the previous member record in an
                                owner/member relationship.


        Set Retrieval

        DbSetGetFirst		Get the first member record in an
                                owner/member relationship.
        DbSetGetLast		Get the last member record in an
                                owner/member relationship.
        DbSetGetNext		Get the next member record in an
                                owner/member relationship.
        DbSetGetOwner	        Get the owner record of a member in an 
                                owner/member relationship.
        DbSetGetPrev		Get the previous member record in an
                                owner/member relationship.


DbClose
-------

Summary

        INT DbClose( void);


Description

        The DbClose function closes the open database.  All database
        files are closed and memory is deallocated.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS.  See the 'Error Messages' section for more detail
        on this value.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;

            if( status = DbOpen( ".\\", "test.dbd"))
                {
                /* Database not opened */
                }

            /* Other CDB calls... */

            if( status = DbClose())
                {
                /* Database not closed */
                }
            }


DbFlush
-------

Summary

        INT DbFlush();


Description

        The DbFlush function forces all data written to the database to
        disk.  Dirty memory pages are written to disk and all open files
        are closed and then reopened.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS.  See the 'Error Messages' section for more detail
        on this value.


Example

        #include "dbmgr.h"

        void Function( struct client far *pClient)
            {
            INT status;

            if( status = DbRecordUpdate( "client", pClient))
                {
                /* Record not updated */
                }

            if( status = DbFlush())
                {
                /* Database not flushed */
                }
            }


DbOpen
------

Summary

        INT DbOpen( char *pDbDir, char *pDbName)


Parameters

        pDbDir          CHAR *  Identifies the directory where CDB will
                        attempt to open the .DBD (Database Definition)
                        file.  CDB will also attempt to open and/or create
                        all associated database files in this directory.
                        If NULL, CDB will use the current directory.
                        Note: If a directory name is present, it must
                        end with a backslash.  e.g. "C:\\PRODUCTA\\".

        pDbName         CHAR *  Identifies the .DBD (Database Definition)
                        file.


Description

        The DbOpen function opens a CDB database.  The database definition
        file (pDbName) is created by DDLP.EXE.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS.  See the 'Error Messages' section for more detail
        on this value.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;

            if( status = DbOpen( "C:\\PRODUCTA\\", "test.dbd"))
                {
                /* Error opening database */
                }

            /* Other CDB calls... */
            }


DbRecordAdd
-----------

Summary

        INT DbRecordAdd( CHAR *pRecName, VOID *pData)


Parameters

        pRecName	CHAR *  Pointer to the record name.

        pData	        VOID *  Pointer to the record data.


Description

        The DbRecordAdd function adds a record to the database.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS or E_NORECNAME.  See the 'Error Messages' section
        for more detail on these values.


Example

        #include "dbmgr.h"

        INT Function( CHAR *pClientData)
            {
            INT status;

            if( status = DbRecordAdd( "client", pClientData))
                {
                /* Error adding record */
                }

            return status;
            }


DbRecordDelete
--------------

Summary

        INT DbRecordDelete( CHAR *pRecName)


Parameters

        pRecName        CHAR *  Pointer to the record name.


Description

        The DbRecordDelete function deletes a record from the database.
        The record deleted is the current record of the pRecName type.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS, E_NORECNAME, or E_NOCURRENT.  See the 'Error
        Messages' section for more detail on these values.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;
            LONG clientnbr = 1000L;

            if( status = DbRecordFindByKey( "client", "clientnbr",
                &clientnbr))
                {
                /* Record not found */
                }

            if( status = DbRecordDelete( "client"))
                {
                /* Error deleting record */
                }
            }


DbRecordFindByKey
-----------------

Summary

        INT DbRecordFindByKey( CHAR *pRecName, CHAR *pFldName, VOID *pKey)


Parameters

        pRecName	CHAR *  Pointer to the record name.

        pFldName	CHAR *  Pointer to the field name.  Must be a key
                        field.

        pKey	        VOID *  Pointer to the key data to be used for the
                        record search.


Description

        The DbRecordFindByKey function searches for a specific record
        using a key field and key value.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS, E_NORECNAME, E_NOFLDNAME, E_NOTAKEY, E_NOTFOUND, or
        E_NEXTGUESS.  See the 'Error Messages' section for more detail on
        these values.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;
            LONG clientnbr = 1000L;

            if( status = DbRecordFindByKey( "client", "lClientNbr",
                &clientnbr))
                {
                /* Record not found */
                }
            }


DbRecordFindFirst
-----------------

Summary

        INT DbRecordFindFirst( CHAR *pRecName, CHAR *pFldName)


Parameters

        pRecName	CHAR *  Pointer to the record name.

        pFldName	CHAR *  Pointer to the field name. Must be a key
                        field.


Description

        The DbRecordFindFirst function sets the database currency to the
        first logical record sorted by pFldName.  For more on currency,
        see the Database Currency section in this manual.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS, E_NORECNAME, E_NOFLDNAME, E_NOTAKEY, or E_NOTFOUND.
        See the 'Error Messages' section for more detail on these values.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;

            /* Delete all client records in database */
            while( ! DbRecordFindFirst( "client", "clientnbr"))
                {
                if( status = DbRecordDelete( "client"))
                    {
                    /* Error deleting record */
                    }
                }
            }


DbRecordFindLast
----------------

Summary

        INT DbRecordFindLast( CHAR *pRecName, CHAR *pFldName)


Parameters

        pRecName	CHAR *  Pointer to the record name.

        pFldName	CHAR *  Pointer to the field name. Must be a key
                        field.


Description

        The DbRecordFindLast function sets the database currency to the
        last logical record sorted by pFldName.  For more on currency, see
        the Database Currency section in this manual.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS, E_NORECNAME, E_NOFLDNAME, E_NOTAKEY, or E_NOTFOUND.
        See the 'Error Messages' section for more detail on these values.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;

            /* Delete all client records in database */
            while( ! DbRecordFindLast( "client", "clientnbr"))
                {
                if( status = DbRecordDelete( "client"))
                    {
                    /* Error deleting record */
                    }
                }
            }


DbRecordFindNext
----------------

Summary

        INT DbRecordFindNext( CHAR *pRecName, CHAR *pFldName)


Parameters

        pRecName	CHAR *  Pointer to the record name.

        pFldName	CHAR *  Pointer to the field name. Must be a key
                        field.


Description

        The DbRecordFindNext function sets the database currency to the
        next logical record sorted by pFldName.  The record must have
        currency before this call is executed.  For more on currency, see
        the Database Currency section in this manual.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS, E_NORECNAME, E_NOFLDNAME, E_NOTAKEY, E_NOTFOUND,
        E_NOCURRENT, or E_NONEXT.  See the 'Error Messages' section for
        more detail on these values.


Example

        #include "dbmgr.h"

        INT Function()
            {
            /* Down arrow key pressed, check for next */
            /* record in database. */
            return( DbRecordFindNext( "client", "clientnbr"));
            }


DbRecordFindPrev
----------------

Summary

        INT DbRecordFindPrev( CHAR *pRecName, CHAR *pFldName)


Parameters

        pRecName	CHAR *  Pointer to the record name.

        pFldName	CHAR *  Pointer to the field name. Must be a key
                        field.


Description

        The DbRecordFindPrev function sets the database currency to the
        previous logical record sorted by pFldName.  The record must have
        currency before this call is executed.  For more on currency, see
        the Database Currency section in this manual.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS, E_NORECNAME, E_NOFLDNAME, E_NOTAKEY, E_NOTFOUND,
        E_NOCURRENT, or E_NOPREV.  See the 'Error Messages' section for
        more detail on these values.


Example

        #include "dbmgr.h"

        INT Function()
            {
            /* Up arrow key pressed, check for previous */
            /* record in database. */
            return( DbRecordFindPrev( "client", "clientnbr"));
            }


DbRecordGetByKey
----------------

Summary

        INT DbRecordGetByKey( CHAR *pRecName, CHAR *pFldName, VOID *pTarget,
                VOID * pKey)


Parameters

        pRecName	CHAR *  Pointer to the record name.

        pFldName	CHAR *  Pointer to the field name. Must be a key
                        field.

        pTarget	        VOID *  Pointer to the storage area for the record
                        data.

        pKey	        VOID *  Pointer to the key data.


Description

        The DbRecordGetByKey function retrieves a record using a key value.
        If the exact match cannot be found the function will return
        E_NEXTGUESS specifying that the data returned is the next best
        guess.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS, E_NORECNAME, E_NOFLDNAME, E_NOTAKEY, E_NOTFOUND, or
        E_NEXTGUESS.  See the 'Error Messages' section for more detail on
        these values.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;
            LONG key = 1000L;
            CLIENT client;

            /* Get client record with client number equal 1000L */
            if( status = DbRecordGetByKey( "client", "clientnbr", &client,
                &key))
                {
                /* Client not retrieved */
                }
            }


DbRecordGetCurrency
-------------------

Summary

        INT DbRecordGetCurrency( CHAR *pRecName, VOID *pTarget)


Parameters

        pRecName	CHAR *  Pointer to the record name.

        pTarget	        VOID *  Pointer to the storage area for the currency
                        information.  


Description

        The DbRecordGetCurrency function retrieves the current currency
        table for a specific record.  For more on currency, see the
        Database Currency section in this manual.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS, or E_NORECNAME.  See the 'Error Messages' section for
        more detail on these values.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;
            struct currency_index currency;

            /* Get the currency for the client record */
            if( status = DbRecordGetCurrency( "client", &currency))
                {
                /* Currency not retrieved */
                }

            /* Other processing goes here... */

            /* Restore the currency for the client record */
            if( status = DbRecordUpdCurrency( "client", &currency))
                {
                /* Currency not updated */
                }
            }


DbRecordGetCurrent
------------------

Summary

        INT DbRecordGetCurrent( CHAR *pRecName, VOID *pTarget)


Parameters

        pRecName	CHAR *  Pointer to the record name.

        pTarget	        VOID *  Pointer to the storage area for the record
                        data.


Description

        The DbRecordGetCurrent function retrieves the record that has
        currency (or 'is current') for that record type (record name).
        Each record type has its own currency table.  For more on currency,
        see the Database Currency section in this manual.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS, E_NORECNAME, or E_NOCURRENT.  See the 'Error Messages'
        section for more detail on these values.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;
            LONG clientnbr = 1000L;
            CLIENT client;

            /* Check for client #1000 */
            if( status = DbRecordFindByKey( "client", "lClientNbr",
                &clientnbr))
                {
                /* Record not found */
                }

            /* Retrieve it */
            if( status = DbRecordGetCurrent( "client", &client))
                {
                /* Record not retrieved */
                }
            }


DbRecordGetFirst
----------------

Summary

        INT DbRecordGetFirst( CHAR *pRecName, CHAR *pFldName, VOID *pTarget)


Parameters

        pRecName	CHAR *  Pointer to the record name.

        pFldName	CHAR *  Pointer to the field name.  Must be a key
                        field.

        pTarget	        VOID *  Pointer to the storage area for the record
                        data.


Description

        The DbRecordGetFirst function retrieves the first record by the
        key field passed.  After this call, the currency for this record
        type is set to the first record.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS, E_NORECNAME, E_NOFLDNAME, E_NOTAKEY, or E_NOTFOUND.
        See the 'Error Messages' section for more detail on these values.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;
            CLIENT client;

            /* Get the first record sorted by client number*/
            if( status = DbRecordGetFirst( "client", "clientnbr", &client))
                {
                /* Record not retrieved */
                }
            }


DbRecordGetLast
---------------

Summary

        INT DbRecordGetLast( CHAR *pRecName, CHAR *pFldName, VOID *pTarget)


Parameters

        pRecName	CHAR *  Pointer to the record name.

        pFldName	CHAR *  Pointer to the field name.  Must be a key
                        field.

        pTarget	        VOID *  Pointer to the storage area for the record
                        data.


Description

        The DbRecordGetLast function retrieves the last record by the key
        field passed.  After this call, the currency for this record type
        is set to the last record.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS, E_NORECNAME, E_NOFLDNAME, E_NOTAKEY, or E_NOTFOUND.
        See the 'Error Messages' section for more detail on these values.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;
            CLIENT client;

            /* Get the last record sorted by client number*/
            if( status = DbRecordGetLast( "client", "clientnbr", &client))
                {
                /* Record not retrieved */
                }
            }


DbRecordGetNext
---------------

Summary

        INT DbRecordGetNext( CHAR *pRecName, CHAR *pFldName, VOID *pTarget)


Parameters

        pRecName	CHAR *  Pointer to the record name.

        pFldName	CHAR *  Pointer to the field name.  Must be a key
                        field.

        pTarget	        VOID *  Pointer to the storage area for the record
                        data.


Description

        The DbRecordGetNext function retrieves the next record by the key
        field passed.  After this call, the currency for this record type
        is set to the record retrieved.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS, E_NORECNAME, E_NOFLDNAME, E_NOTAKEY, E_NOTFOUND,
        E_NOCURRENT, or E_NONEXT.  See the 'Error Messages' section for
        more detail on these values.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;
            CLIENT client;

            /* Get the next record sorted by client number*/
            if( status = DbRecordGetNext( "client", "clientnbr", &client))
                {
                /* Record not retrieved */
                }
            }


DbRecordGetPrev
---------------

Summary

        INT DbRecordGetPrev( CHAR *pRecName, CHAR *pFldName, VOID *pTarget)


Parameters

        pRecName	CHAR *  Pointer to the record name.

        pFldName	CHAR *  Pointer to the field name.  Must be a key
                        field.

        pTarget	        VOID *  Pointer to the storage area for the record
                        data.


Description

        The DbRecordGetPrev function retrieves the previous record by the
        key field passed.  After this call, the currency for this record
        type is set to the record retrieved.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS, E_NORECNAME, E_NOFLDNAME, E_NOTAKEY, E_NOTFOUND,
        E_NOCURRENT, or E_NOPREV.  See the 'Error Messages' section for
        more detail on these values.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;
            CLIENT client;

            /* Get the previous record sorted by client number*/
            if( status = DbRecordGetPrev( "client", "clientnbr", &client))
                {
                /* Record not retrieved */
                }
            }


DbRecordUpdate
--------------

Summary

        INT DbRecordUpdate( CHAR *pRecName, VOID *pData)


Parameters

        pRecName	CHAR *  Pointer to the record name.

        pData	        VOID *  Pointer to the updated record data.


Description

        The DbRecordUpdate function updates a database record.  The record
        to be updated must be current.  For more on currency, see the
        Database Currency section in this manual.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS, E_NORECNAME, or E_NOCURRENT.  See the 'Error Messages'
        section for more detail on these values.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;
            CLIENT client;

            /* Get the first record */
            if( status = DbRecordGetFirst( "client", "clientnbr", &client))
                {
                /* Record not retrieved */
                }

            /* Modify input logic goes here... */

            /* Update the record */
            if( status = DbRecordUpdate( "client", &client))
                {
                /* Error updating record */
                }
            }


DbRecordUpdCurrency
-------------------

Summary

        INT DbRecordUpdCurrency( CHAR *pRecName, VOID *pData)


Parameters

        pRecName	CHAR *  Pointer to the record name.

        pData	        VOID *  Pointer to the storage area for the currency
                        information.  


Description

        The DbRecordUpdCurrency function updates the currency for a
        specific record type.  For more on currency, see the Database
        Currency section in this manual.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS or E_NORECNAME.  See the 'Error Messages' section for
        more detail on these values.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;
            struct currency_index currency;

            /* Get the currency for the client record */
            if( status = DbRecordGetCurrency( "client", &currency))
                {
                /* Error retrieving currency */
                }

            /* Other processing goes here... */

            /* Restore the currency for the client record */
            if( status = DbRecordUpdCurrency( "client", &currency))
                {
                /* Error updating currency */
                }
            }


DbSetAdd
--------

Summary

        INT DbSetAdd( CHAR *pOwnerName, CHAR *pMemberName)


Parameters

        pOwnerName	CHAR *  Pointer to the owner record name.

        pMemberName	CHAR *  Pointer to the member record name.


Description

        The DbSetAdd function makes a set connection between two records.
        Both records must have currency before making the call.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS,  E_NORECNAME, E_INVALIDSET, or E_NOCURRENT.  See the
        'Error Messages' section for more detail on these values.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;

            /* Add an client record */
            /* Assuming 'sclient' is global and structure prefilled */
            if( status = DbRecordAdd( "client", &sclient))
                {
                /* Error adding record */
                }

            /* Add an address record */
            /* Assuming 'saddress' is global and structure prefilled */
            if( status = DbRecordAdd( "address", &saddress))
                {
                /* Error adding record */
                }

            /* Make a set connection between records */
            /* After this call, the 'client' record is the owner of the */
            /* 'address' record */
            /* The 'address' record is a member of the 'client' record */
            if( status = DbSetAdd( "client", "address"))
                {
                /* Error making set connection */
                }
            }


DbSetDelete
-----------

Summary

        INT DbSetDelete( CHAR *pOwnerName, CHAR *pMemberName)


Parameters

        pOwnerName	CHAR *  Pointer to the owner record name.

        pMemberName	CHAR *  Pointer to the member record name.


Description

        The DbSetDelete function removes a set connection between two
        records.  Both records must have currency before making the call.
        This function does not delete either record, it only removes the
        connection between the two.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS, E_NORECNAME, E_INVALIDSET, or E_NOCURRENT.  See the
        'Error Messages' section for more detail on these values.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;
            LONG clientnbr = 1000L;

            /* Set the currency to client record #1000 */
            if( status = DbRecordFindByKey( "client", "clientnbr",
                &clientnbr))
                {
                /* Record not found */
                }

            /* Set the currency for the address record to the first */
            /* member of the client record */
            if( status = DbSetFindFirst( "client", "address"))
                {
                /* First member not found */
                }

            /* Delete the owner/member set connection */
            if( status = DbSetDelete( "client", "address"))
                {
                /* Error deleting record */
                }
            }


DbSetFindFirst
--------------

Summary

        INT DbSetFindFirst( CHAR *pOwnerName, CHAR *pMemberName)

Parameters

        pOwnerName	CHAR *  Pointer to the owner record name.

        pMemberName	CHAR *  Pointer to the member record name.


Description

        The DbSetFindFirst function sets the database currency for the
        member record to the first member in the owner/member set relation.
        The owner record must have currency before this call.  For more on
        currency, see the Database Currency section in this manual.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS, E_NORECNAME, E_INVALIDSET, E_NOCURRENT, or E_NOTFOUND.
        See the 'Error Messages' section for more detail on these values.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;
            LONG clientnbr = 1000L;

            /* Set the currency to client record #1000 */
            if( status = DbRecordFindByKey( "client", "clientnbr",
                &clientnbr))
                {
                /* Record not found */
                }

            /* Set the currency for the address record to the first */
            /* member of the client record */
            if( status = DbSetFindFirst( "client", "address"))
                {
                /* First member not found */
                }
            }


DbSetFindLast
-------------

Summary

        INT DbSetFindLast( CHAR *pOwnerName, CHAR *pMemberName)


Parameters

        pOwnerName	CHAR *  Pointer to the owner record name.

        pMemberName	CHAR *  Pointer to the member record name.


Description

        The DbSetFindLast function sets the database currency for the
        member record to the last member in the owner/member set relation.
        The owner record must have currency before this call.  For more on
        currency, see the Database Currency section in this manual.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS, E_NORECNAME, E_INVALIDSET, E_NOCURRENT, or E_NOTFOUND.
        See the 'Error Messages' section for more detail on these values.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;
            LONG clientnbr = 1000L;

            /* Set the currency to client record #1000 */
            if( status = DbRecordFindByKey( "client", "clientnbr",
                &clientnbr))
                {
                /* Record not found */
                }

            /* Set the currency for the address record to the last */
            /* member of the client record */
            if( status = DbSetFindLast( "client", "address"))
                {
                /* Last member not found */
                }
            }


DbSetFindNext
-------------

Summary

        INT DbSetFindNext( CHAR *pOwnerName, CHAR *pMemberName)


Parameters

        pOwnerName	CHAR *  Pointer to the owner record name.

        pMemberName	CHAR *  Pointer to the member record name.


Description

        The DbSetFindNext function sets the database currency for the
        member record to the next member in the owner/member set relation.
        Both owner and member records must have currency before this call.
        For more on currency, see the Database Currency section in this
        manual.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS, E_NORECNAME, E_INVALIDSET, E_NOCURRENT, or E_NONEXT.
        See the 'Error Messages' section for more detail on these values.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;
            LONG clientnbr = 1000L;

            /* Set the currency to client record #1000 */
            if( status = DbRecordFindByKey( "client", "clientnbr",
                &clientnbr))
                {
                /* Record not found */
                }

            /* Set the currency for the address record to the first */
            /* member of the client record */
            if( status = DbSetFindFirst( "client", "address"))
                {
                /* First member not found */
                }

            /* Set the currency for the address record to the next member */
            /* of the client record */
            if( status = DbSetFindNext( "client", "address"))
                {
                /* Next member not found */
                }
            }


DbSetFindPrev
-------------

Summary

        INT DbSetFindPrev( CHAR *pOwnerName, CHAR *pMemberName)


Parameters

        pOwnerName	CHAR *  Pointer to the owner record name.

        pMemberName	CHAR *  Pointer to the member record name.


Description

        The DbSetFindPrev function sets the database currency for the
        member record to the previous member in the owner/member set
        relation.  Both owner and member records must have currency
        before this call.  For more on currency, see the Database Currency
        section in this manual.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS, E_NORECNAME, E_INVALIDSET, E_NOCURRENT, or E_NOPREV.
        See the 'Error Messages' section for more detail on these values.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;
            LONG clientnbr = 1000L;

            /* Set the currency to client record #1000 */
            if( status = DbRecordFindByKey( "client", "clientnbr",
                &clientnbr))
                {
                /* Record not found */
                }

            /* Set the currency for the address record to the last */
            /* member of the client record */
            if( status = DbSetFindLast( "client", "address"))
                {
                /* Last member not found */
                }

            /* Set the currency for the address record to the previous */
            /* member of the client record */
            if( status = DbSetFindPrev( "client", "address"))
                {
                /* Previous member not found */
                }
            }


DbSetGetFirst
-------------

Summary

        INT DbSetGetFirst( CHAR *pOwnerName, CHAR *pMemberName,
                VOID *pTarget)


Parameters

        pOwnerName	CHAR *  Pointer to the owner record name.

        pMemberName	CHAR *  Pointer to the member record name.

        pTarget	        VOID *  Pointer to the storage area for the record
                        data.


Description

        The DbSetGetFirst function retrieves the first member of an
        owner/member set relation.  The owner record must have currency
        before this call.  For more on currency, see the Database Currency
        section in this manual.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS, E_NORECNAME, E_INVALIDSET, E_NOCURRENT, or E_NOTFOUND.
        See the 'Error Messages' section for more detail on these values.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;
            LONG clientnbr = 1000L;
            ADDRESS address;

            /* Set the currency to client record #1000 */
            if( status = DbRecordFindByKey( "client", "clientnbr",
                &clientnbr))
                {
                /* Record not found */
                }

            /* Get the first address record */
            if( status = DbSetGetFirst( "client", "address", &address))
                {
                /* First member not found */
                }
            }


DbSetGetLast
------------

Summary

        INT DbSetGetLast( CHAR *pOwnerName, CHAR *pMemberName, VOID *pTarget)


Parameters

        pOwnerName	CHAR *  Pointer to the owner record name.

        pMemberName	CHAR *  Pointer to the member record name.

        pTarget	        VOID *  Pointer to the storage area for the record
                        data.


Description

        The DbSetGetLast function retrieves the last member of an
        owner/member set relation.  The owner record must have currency
        before this call.  For more on currency, see the Database Currency
        section in this manual.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS, E_NORECNAME, E_INVALIDSET, E_NOCURRENT, or E_NOTFOUND.
        See the 'Error Messages' section for more detail on these values.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;
            LONG clientnbr = 1000L;
            ADDRESS address;

            /* Set the currency to client record #1000 */
            if( status = DbRecordFindByKey( "client", "clientnbr",
                &clientnbr))
                {
                /* Record not found */
                }

            /* Get the last address record */
            if( status = DbSetGetLast( "client", "address", &address))
                {
                /* Last member not found */
                }
            }


DbSetGetNext
------------

Summary

        INT DbSetGetNext( CHAR *pOwnerName, CHAR *pMemberName, VOID *pTarget)


Parameters

        pOwnerName	CHAR *  Pointer to the owner record name.

        pMemberName	CHAR *  Pointer to the member record name.

        pTarget	        VOID *  Pointer to the storage area for the record
                        data.


Description

        The DbSetGetNext function retrieves the next member of an
        owner/member set relation.  Both owner and member records must
        have currency before this call.  For more on currency, see the
        Database Currency section in this manual.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS, E_NORECNAME, E_INVALIDSET, E_NOCURRENT, or E_NONEXT.
        See the 'Error Messages' section for more detail on these values.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;
            LONG clientnbr = 1000L;
            ADDRESS address;

            /* Set the currency to client record #1000 */
            if( status = DbRecordFindByKey( "client", "clientnbr",
                &clientnbr))
                {
                /* Record not found */
                }

            /* Get the first address record */
            if( status = DbSetGetFirst( "client", "address", &address))
                {
                /* First member not found */
                }

            /* Get the next address record */
            if( status = DbSetGetNext( "client", "address", &address))
                {
                /* Next member not found */
                }
            }


DbSetGetOwner
-------------

Summary

        INT DbSetGetOwner( CHAR *pOwnerName, CHAR *pMemberName,
            VOID *pTarget)


Parameters

        pOwnerName	CHAR *  Pointer to the owner record name.

        pMemberName	CHAR *  Pointer to the member record name.

        pTarget	        VOID *  Pointer to the storage area for the record
                        data.


Description

        The DbSetGetOwner function retrieves the owner record of a member
        record set relation.  The member record must have currency before
        this call.  For more on currency, see the Database Currency section
        in this manual.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS, E_NORECNAME, E_INVALIDSET, E_NOCURRENT, or E_NOOWNER.
        See the 'Error Messages' section for more detail on these values.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;
            CLIENT client;

            /* Set the currency to the first invoice record */
            if( status = DbRecordFindFirst( "invoice", "invoicenbr"))
                {
                /* Invoice not found */
                }

            /* Get the client record for this invoice */
            if( status = DbSetGetOwner( "client", "invoice", &client))
                {
                /* Client record not found */
                }
            }


DbSetGetPrev
------------

Summary

        INT DbSetGetPrev( CHAR *pOwnerName, CHAR *pMemberName, VOID *pTarget)


Parameters

        pOwnerName	CHAR *  Pointer to the owner record name.

        pMemberName	CHAR *  Pointer to the member record name.

        pTarget	        VOID *  Pointer to the storage area for the record
                        data.


Description

        The DbSetGetPrev function retrieves the previous member of an
        owner/member set relation.  Both owner and member records must
        have currency before this call.  For more on currency, see the
        Database Currency section in this manual.


Return Value

        A 0 is returned if no error occurred.  Otherwise the return code
        can be E_DOS, E_NORECNAME, E_INVALIDSET, E_NOCURRENT, or E_NOPREV.
        See the 'Error Messages' section for more detail on these values.


Example

        #include "dbmgr.h"

        void Function()
            {
            INT status;
            LONG clientnbr = 1000L;
            ADDRESS address;

            /* Set the currency to client record #1000 */
            if( status = DbRecordFindByKey( "client", "clientnbr",
                &clientnbr))
                {
                /* Record not found */
                }

            /* Get the last address record */
            if( status = DbSetGetLast( "client", "address", &address))
                {
                /* Last member not found */
                }

            /* Get the previous address record */
            if( status = DbSetGetPrev( "client", "address", &address))
                {
                /* Previous member not found */
                }
            }


ERROR MESSAGES

        This section describes error messages that you may encounter when
        developing a program using CDB.


CDB Run-Time Error Messages

        The CDB C-API function calls return an INT value indicating the
        success or failure of a particular database call.  A 0 is returned
        if the function was a success.  A non-zero value is returned if an
        error occured.

        The following list displays the possible error codes that can be
        returned and a brief explanation of each.


        Error Code		Description

        E_TESTDRIVE		You are using a 'Test Drive' version of CDB.
                                The 'Test Drive' version limits the number
                                of records that can be added to a database
                                to 50.

        E_INVALIDCASE	        Contact Daytris technical support.  An
                                internal switch statement does not contain
                                a valid case.  You should never see this
                                error code.

        E_DOS		        An MS-DOS error has occured.  The global
                                'errno' value contains the specific DOS
                                error number.  See ERRNO.H and/or the
                                Microsoft C documentation for more
                                information.

        E_NORECNAME	        The record name passed, pRecName, is not a
                                valid record type.

        E_NOFLDNAME	        The field name passed, pFldName, is not a
                                valid field for the record.

        E_INVALIDSET	        The owner and member names passed to the
                                function do not have a set relationship
                                between the two.  To create a set
                                relatiionship between two records, use the
                                CONNECT keyword in the DDL file.

        E_NOTAKEY               The field name passed to the function is
                                not a key field in the record.  Use the KEY
                                keyword in the DDL file to define key
                                fields.

        E_NOTFOUND    	        The record was not found.


        E_NEXTGUESS		The record was not found, but the next
                                closest match to the key value passed was
                                found.  If a DbRecordFindByKey call was
                                made, currency is set to this 'next guess'
                                record.  If a DbRecordGetByKey call was
                                made, the 'next guess' record is returned.

        E_NOCURRENT	        There is no current record for the record
                                name specified.  e.g.  This error value
                                will be returned if a DbRecordGetNext call
                                is made before the record requested has
                                currency.  One way to set currency in this
                                case would be to make a DbRecordFindFirst
                                call.  There are a number of other cases
                                where this error code could be returned.

        E_NONEXT		The next record was not found.  e.g. 
                                DbRecordGetNext(...).

        E_NOPREV		The previous record was not found.  e.g. 
                                DbRecordGetPrevious(...).

        E_NOOWNER		This error can only occur with a
                                DbSetGetOwner call.  If an owner record is
                                not found, this value is returned.


DDLP Error Messages

        The following lists contain a description of error and warning
        messages that may be encountered during the execution of the Data
        Definition Language Parse utility (DDLP.EXE):

        Error Number	Description

        100		Unexpected end of file reached.
        101		Unexpected token.  DDLP breaks the DDL file into
                        tokens.  A token can be a bracket, keyword,
                        semicolon, variable, constant, etc.  It combines the
                        tokens and matches them against predefined patterns.
                        If a pattern has no match, this error is returned.
        102		Expecting semicolon.
        103		Expecting "struct" keyword.  DDLP is expecting a 
                        structure definition to begin.
        104		Expecting identifier.  An identifer can be a
                        structure name or field name.
        105		Expecting '{'.  Expecting a left brace.
        106		Constant too big.  A constant is a number.  The 
                        maximum constant size allowed is 10 digits.
        107		Structure already defined.
        108		Invalid constant.  The interpreted value of the 
                        constant is zero.
        109		Maximum size of a constant is 65535.
        110		Maximum size of a field is 65535.
        111		Maximum size of a record is 65535.
        112		Connection already made to 'record name'.  You 
                        cannot to the same record more than once.
        113		Record does not exist.  This error will occur when 
                        you try to CONNECT to a record that is not defined 
                        within the DDL file.
        114		Cannot connect structure to itself.
        115		Connect key field not found.  If you are ordering
                        the sets using the 'CONNECT record_name KEY
                        key_field_name' convention, the key_field_name is
                        not found within the record_name structure
                        definition in the DDL file.

        Warning Number	Description

        100		"prefix" not defined, assuming "test".  The PREFIX
                        keyword was not used to define the prefix used to
                        derive database file names.  A "test" prefix is
                        used as a default.
        101		Prefix too long, truncating to 'identifier'.  The
                        maximum length of a PREFIX is 5 characters.
        102		Identifier too long.  The maximum length of an
                        identifier is 31 characters.  Extra characters will
                        be truncated.
