4.7 Finding Records by Key



Dfindk(C-3) locates a record by its primary key. Each record in an RMSfile has a primary key that determines its location. To look up a record, the key value is first put into the user record. Dfindk is called with the address of the user record. Dfindk uses the key put into the user record to locate the record. If the record is found, the field values are placed into the user record as dictated by a previous drlist(C-3) or dblist(C-3) call. If the record cannot be found or if some other error occurs, a record number of -1 is returned.

An example of dfindk would be to look up a description for a key field. The following example illustrates this by looking up the title of a magazine from the magazine code stored in the subscription file.

#include <stdio.h>

#include <cbase/dtypes.h>

#include <cbase/dirio.h>

#define SZ(s,m) (sizeof(((s *)0)->m))

typedef struct scriptrec {

STRING subscriber[16]; /* code from subscriber master */

STRING magazine[16]; /* code from magazine master */

short issues; /* number of issues left */

DATE started; /* date subscription was started */

} ScriptRec;

ScriptRec scriptbuf;

DR scriptlist[] = {

{"subscriber", STRING_TYPE, SZ(ScriptRec,subscriber), 1, DRCONVPRE},

{"magazine", STRING_TYPE, SZ(ScriptRec,magazine), 1, DRCONVPRE},

{"issues", INT_TYPE, SZ(ScriptRec,issues), 1, DRCONVPRE},

{"started", DATE_TYPE, SZ(ScriptRec,started), 1, DRCONVPRE},

{0}

};

DFILE *sfp = NULL;

/* C structure for field list below */

typedef struct magrec {

STRING magazine[16];

STRING title[36];

} MagRec;

MagRec magbuf;

DR maglist[] = {

{"magazine", STRING_TYPE, SZ(MagRec,magazine), 1, DRCONVPRE},

{"title", STRING_TYPE, SZ(MagRec,title), 1, DRCONVPRE},

{0}

};

DFILE *mfp = NULL;

main (argc, argv)

int argc;

char *argv[];

{

long recno;

char *magdescr;

if ((sfp = dlopen ("script", "r")) == NULL)

fatal ("can't open subscription file\n");

if (drlist (scriptlist, sfp) < 0L) {

puts (derrmsg());

fatal ("bad field list for subscription file\n");

}

if ((mfp = dlopen ("mag", "r")) == NULL)

fatal ("can't open magazine file\n");

if (drlist (maglist, mfp) < 0L) {

puts (derrmsg());

fatal ("bad field list for magazine file\n");

}

while ((recno = dfindn (&scriptbuf, sfp)) > 0L) {

strncpy (magbuf.magazine, scriptbuf.magazine,

sizeof (magbuf.magazine));

if (dfindk (&magbuf, mfp) < 0L)

magdescr = "No such magazine"

else

magdescr = magbuf.title;

do_details (&scriptbuf, magdescr);

}

fatal (NULL);

}

do_details (sc, t)

struct scriptrec *sc;

char *t;

{

static int linecount = 0;

if (linecount == 0) {

printf ("\n Subscription List\n");

printf ("Subscriber %-36sIssues Started\n\n",

"Title");

linecount = 4;

}

printf ("%-14s%-36s %3d %s\n", sc->subscriber,

t, sc->issues, datetoa (sc->started));

if (linecount++ > 63) {

printf ("\f");

linecount = 0;

}

}

fatal (p)

char *p;

{

if (p)

puts (p);

if (sfp != NULL) {

dclose (sfp);

sfp = NULL;

}

if (mfp != NULL) {

dclose (mfp);

mfp = NULL;

}

if (p)

exit (1);

else

exit (0);

}

The strncpy(3) function is called to put STRING key values into the record buffer. If the value does not completely fill the character positions allotted to the field, strncpy pads the value on right with nulls (zero bytes). Every character in a key field is significant when the system is determining where a record should be placed. If a key value does not completely fill the space allotted, the remaining character positions must be set to zero, otherwise the new contents of the buffer may be mixed with its previous contents.

Also notice the amount of effort used to ensure that all RMSfiles opened with dlopen are closed. This example assumes that if dfindk cannot find the record, the record is not in the RMSfile. It is ignoring the possibility of some other error.

One last note: so far, each program we have written has defined a structure and field list for the RMSfiles being used. If the format of the RMSfiles ever changes, each program using the changed files must be modified. For two or three programs, this is no tough job. However, multiply this by 100 programs and there is suddenly a lot of work to be done. One way around this is to make an include file that defines all of the fields in an RMSfile. We have done this for all the demo files, and they are used for all future examples. The include files are listed here for future reference:

Sub.h

#ifndef Sub_H_

#define Sub_H_

#ifndef SZ

#define SZ(s,m) (sizeof(((s *)0)->m))

#endif

/*

* User record and field list derived from def/sub

* "@(#)sub.a 4.2"

*/

typedef struct SubRec {

STRING subscriber[16]; /* unique subscriber code */

STRING name[36]; /* subscriber name */

STRING address[2][36]; /* ..street address, 2 lines */

STRING city[32]; /* ..city */

STRING state[8]; /* ..state */

STRING zip[12]; /* ..USPS zip code */

} SubRec;

SubRec subbuf;

DR sublist[] = {

{"subscriber", STRING_TYPE, SZ(SubRec,subscriber), 1, DRCONVPRE},

{"name", STRING_TYPE, SZ(SubRec,name), 1, DRCONVPRE},

{"address", STRING_TYPE, SZ(SubRec,address), 2, DRCONVPRE},

{"city", STRING_TYPE, SZ(SubRec,city), 1, DRCONVPRE},

{"state", STRING_TYPE, SZ(SubRec,state), 1, DRCONVPRE},

{"zip", STRING_TYPE, SZ(SubRec,zip), 1, DRCONVPRE},

{0}

};

DFILE *sub = NULL;

#endif

Mag.h

#ifndef Mag_H_

#define Mag_H_

#ifndef SZ

#define SZ(s,m) (sizeof(((s *)0)->m))

#endif

/*

* User record and field list derived from def/mag

* "@(#)mag.a 4.2"

*/

typedef struct MagRec {

STRING magazine[16]; /* unique magazine code */

STRING title[36]; /* magazine title */

STRING unused [4]; /* required for 8 byte alignment */

double year_rate; /* 1 year's subscription rate */

short subscribers; /* number of subscribers */

} MagRec;

MagRec magbuf;

DR maglist[] = {

{"magazine", STRING_TYPE, SZ(MagRec,magazine), 1, DRCONVPRE},

{"title", STRING_TYPE, SZ(MagRec,title), 1, DRCONVPRE},

{"year_rate", MONEY_TYPE, SZ(MagRec,year_rate), 1, DRCONVPRE},

{"subscribers", INT_TYPE, SZ(MagRec,subscribers), 1, DRCONVPRE},

{0}

};

DFILE *mag = NULL;

#endif

Script.h

#ifndef Script_H_

#define Script_H_

#ifndef SZ

#define SZ(s,m) (sizeof(((s *)0)->m))

#endif

/*

* User record and field list derived from def/script

* "@(#)script.a 4.2"

*/

typedef struct ScriptRec {

STRING subscriber[16]; /* code from subscriber master */

STRING magazine[16]; /* code from magazine master */

short issues; /* number of issues left */

DATE started; /* date subscription was started */

} ScriptRec;

ScriptRec scriptbuf;

DR scriptlist[] = {

{"subscriber", STRING_TYPE, SZ(ScriptRec,subscriber), 1, DRCONVPRE},

{"magazine", STRING_TYPE, SZ(ScriptRec,magazine), 1, DRCONVPRE},

{"issues", INT_TYPE, SZ(ScriptRec,issues), 1, DRCONVPRE},

{"started", DATE_TYPE, SZ(ScriptRec,started), 1, DRCONVPRE},

{0}

};

DFILE *script;

#endif