URL: http://www.firstbasesoftware.com/man/man4/database.htm
Last modified: 12 September 1995
Copyright © by FirstBase Software.
[
Index of Contents] [
FirstBase RDBMS Overview]
typedef struct fb_s_link fb_link; /* defined link */ typedef struct fb_s_field fb_field; /* defined field */ typedef struct fb_s_database fb_database;/* defined database */ struct fb_s_link { fb_database *f_dp; /* pointer to linked database */ fb_field *f_fix; /* indexed value in 'from' database */ fb_field *f_tix; /* ix val in 'to' db providing link */ fb_field *f_ffp; /* field within 'from' database */ fb_field *f_tfp; /* field within 'to' database */ char *f_fld; /* storage area for field val */ char *f_xfld; /* index value creating field link */ fb_link *f_next; /* to link all flinks for searching */ long f_absrec; /* absolute record number to use */ fb_database *f_basedp; /* database which referred link */ }; struct fb_s_ixauto { /* auto indexed field struct */ char *autoname; /* for storing autoindex name */ char *dup_fld; /* for 'old' field value */ int afd, hfd; /* auto index and header fds */ short uniq; /* uniqness flag */ int ix_tree; /* btree flag */ fb_bseq *ix_seq, *ix_seqtmp; /* btree sequence set */ fb_bidx *ix_idx, *ix_idxtmp; /* btree index set */ fb_field **ix_ip; /* field pointers array for auto ix */ long ix_bsmax, ix_bsend; /* for btrees - for auto ix array */ int ix_ifields; /* number of fields in the ix array */ char *ix_key_fld; /* key space for index entry */ }; struct fb_s_field { /* a database field */ char *id; /* unique title labeling field */ char type; /* type of the field */ int size; /* maximum size of field */ int loc; /* location when arec is used */ char *fld; /* actual pointer to field */ int incr; /* to store incremental defaults */ char *comment, *idefault, *help, *prev, *range, *a_template, *f_macro; char comloc, lock, choiceflag; /* various flags */ fb_autoindex *aid; /* pointer to auto index data */ fb_link *dflink; /* link to other database */ fb_link *xflink; /* extended choice/link database */ char *mode; /* permissions settings per field */ short int f_prec; /* precision for field */ }; struct fb_s_database { /* FB database header record */ char *dbase; /* full database path names */ char *dindex; char *dmap; char *ddict; char *idict; char *sdict; /* simple screen name */ char *dlog; /* log name of database */ long reccnt; /* full record count */ char dirty; /* dirty bit == 0 or 1 */ long delcnt; /* deleted record count */ long rec; /* current record number */ int recsiz; /* approximate record size */ int nfields; /* number of fields */ int fd; /* database file descriptor */ int ifields; /* number of index fields */ int ifd; /* index file descriptor */ int ihfd; /* index header file descriptor */ int logfd; /* log file descriptor */ int irecsiz; /* index record size */ long bsmax, bsend, bsrec; /* search max/end points */ int mfd; /* record map file descriptor */ fb_field **kp; /* array of fields */ fb_field **ip; /* array of index fields */ char *orec; /* original copy of rec */ char *arec; /* alternate copy of rec */ char *irec; /* buffer for an index record */ char *afld; /* field buffers for this db */ char *bfld; /* field buffers for this db */ int refcnt; /* reference count for linktop list */ /* B+tree index fields */ int b_tree; /* flag set if dbase is using btrees */ fb_bseq *b_seq, *b_seqtmp; /* sequence set (and temp space) */ fb_bidx *b_idx, *b_idxtmp; /* index set (and temp space) */ fb_autoindex **b_autoindex; /* autoindex ptr array */ int b_maxauto; /* max number of auto index */ int b_curauto; /* cur number of auto index array */ short int fixedwidth; /* fixed width flag */ int b_sid; /* server id - open via server */ };
When a database is opened, the entire database structure is initialized. The elements of the database structure are fairly straightforward. The beginning contains all of the needed file names making initialization from C programs an easier task (see opendb(3)).
The array of key pointers, or field descriptors, is allocated on the fly to contain as many fields as defined in the database dictionary, plus the additional field for marking deleted records. The base of this array is located in the database structure variable kp. These fields are indexed within kp starting at 0, and going through nfields, another element of the database structure. Again, kp[0] is the first field, kp[1] is the second field, etc. kp[nfields] is the undefined field used for marking a deleted record.
Each field descriptor is a structure itself. These structures contain the name of the field, its type and size, as well as various pointers to comments, help files, and defaults. The actual values of a database field are stored within the field structure in a character pointer, fld.
When a record is read in using getrec(3), the database structure area orec is used to store the entire original record. Each field descriptor is set up to point into the orec area to individual null terminated fields.
When a field is changed using store(3), the field descriptor pointer fld is modified to point into the alternate record area of the database structure, arec. Modified fields are always stored in arec in a fixed length manner to prevent fields from overwriting one another.
Finally, when putrec(3) is called, the contents of each field are copied into the arec area, with each field placed end to end. Only a single NULL byte is stored between fields. This complete unit is written to the disk, with the length and byte position numbers stored in the database map (see below).
Note that in the case of BINARY fields, the entire field is ALWAYS fixed length, and there is NEVER a NULL byte stored between a BINARY field and a neighboring non-BINARY field. Furthermore, it is up to the programmer using store(3) to make sure the object being stored is the same byte size as the field length in the database dictionary.
Again, each field descriptor is on a line by itself, with each attribute separated by white space. The optional attributes are represented as switch settings and are defined on a field by field basis. The following represents an entire field attribute:
Field_Name Field_Type Field_Size [-c[ab] comment]
[-d default] [-h helpfile] [-a[u] autoindex]
There are two switches that have options within themselves: If a comment is desired, the a or the b can be used to specify that the comment be placed after or before the actual field. Also, the optional u in the -a switch will make the database editor enforce unique values for all values entered into the associated field.
Again, see the manual page dbdbas(1) for more details on what each switch is used for.
The database map is laid out as pairs of numbers, each one of type long. These numbers are written to the disk as raw data, each one taking up exactly sizeof(long) bytes.
The database map also contains two important variables that are needed for variable length record storage: avail, representing the next available byte location in the database, and free, representing any free space within the database. Avail points to the end of the database, while free points to the head of the free list. Note that this free list may be empty.
Free storage can accumulate when a short record is replaced with a longer record -- for example, when a 5 character field is replaced with a 10 character field. In this case, putrec(3) will free up the old storage area, adding this to the head of the free list, and use avail to allocate a new storage area at the end of the database file.
These two variables, avail and free, are said to be the 0th record of the database map, i.e. the database map header information. The rest of the database map file aligns itself exactly to the database file itself -- record one of the map contains the 'coordinates' of record one of the database data, etc.
As mentioned, within each of the map records there is a pair of numbers of type long. These numbers represent the data record position, rpos, and the data record length, rlen. Rpos is an absolute byte offset into the database file itself, and rlen is the exact number of bytes used by the record.
Pictorially, the database map looks like this:
_________________________
| Field 1 Field 2 |
| __________________ |
| |
Map Rec 0 | avail free |
| |
Map Rec 1 | rpos rlen | For Data Record 1
| |
Map Rec 2 | rpos rlen | For Data Record 2
... ...
Map Rec N | rpos rlen | For Data Record N
|_______________________|
The database data header consists of three separate numbers, all written as printable characters, not as raw data. The first is a 4 character number representing the database sequence number. This number is used as a consistency check between a database and an index. When indexes are created, the same sequence number of the database is placed in the index header, making this index usable only with its matching database. The database sequence number is bumped by one for each new database created, and wraps back to 1001 after hitting 9999.
The second number is a ten digit number representing the database record count. This is the actual number of records in the database, including any records marked for deletion. The third number is a ten digit number that represents that number of records that have been deleted.
Altogether, the database data file header takes up exactly 24 bytes, with each byte being a printable character.
When a record N is replaced with a new record smaller than the record length of N, putrec(3) will fill in the resulting void with a fill character, ^X, and terminate this fill area with a fill-area-end character, ^Y. This space is NOT linked into a free list. Again, once a space is defined in the database map file as being R characters long (rlen), it remains R characters long. This space is called fragmented space, and can be reclaimed using the FirstBase tool dbclean(1).
When a record N is replaced with a new record that is larger than the record length of N, putrec(3) will place the new record in a different area in the database file. This new area is either at the end of the file, or a first-fit area from the free list is found. If the old area is large enough, it will be placed on the top of the free list in the following manner.
The first byte of the newly freed area is marked with a free marker, ^F. Next, two sizeof(long) byte areas are used, and raw numbers are written there. These numbers represent the length of the free area, and a byte pointer to the next free area. Any additional free area is filled in as if it were fragmented space using the fill character, ^X, and the fill-area-end character, ^Y.