home *** CD-ROM | disk | FTP | other *** search
- (*
- MULTIKEY v 1.0 - Routines to help you use Turbo Database Toolbox
- Edwin Floyd 76067,747
-
- These routines provide three services to the programmer:
-
- 1. Automate the creation and maintenance of multiple index files within
- a Database Toolbox application.
-
- 2. Automatically rebuild a lost/corrupted (or build a new) index file.
-
- 3. Rebuild the freespace chain on a corrupted data file.
-
- Under the premise that an example is worth a basketfull of exposition, a
- brief description of the routines and data structures is provided followed
- by an example program.
-
- CONST MaxKeys = nn;
-
- Must appear before the include for MULTIKEY.BOX. This specifies the
- maximum number of keys to be defined for any record in this program.
-
- {$I MULTIKEY.BOX}
-
- Must appear after all Database Toolbox includes. All Toolbox routines
- are referenced, so don't leave any Includes out.
-
- Anyname_File, ...: File_Type;
-
- File_Type is defined in MULTIKEY.BOX. For each File_Type record, you
- must fill out a description of the file and all key fields in the record
- sometime before calling Open_File:
-
- WITH Anyname_File DO BEGIN
- Name := 'xxxxxxxx'; You may specify the Drive, path and file name,
- Leave off the extension, the data file will
- have the extension ".DAT" and you may specify
- the extension of each index file below.
-
- RecSize := Sizeof (Rec); Specify the size of the data record
-
- Number_Of_Keys := nn; Specify the number of keys (<= MaxKeys) for
- This file.
-
- WITH Key [1] DO BEGIN Describe each key...
-
- Offset := KeyOffset (Rec, Rec.Key);
- Specify offset of first byte of key from
- beginning of record (first byte of record
- is offset zero (0) but the minimum key
- offset allowed is 2 because of the record
- status word). You may use the KeyOffset
- function described below.
-
- KeyLength := Sizeof (Rec.Key);
- Specify the length in bytes of the key for
- CHAR or ARRAY of CHAR key.
-
- Extension := 'iii'; Specify the extension for the index file
-
- Unique := TRUE/FALSE;Is this a unique key (TRUE) or are duplicates
- allowed (FALSE)?
-
- Upshift := TRUE/FALSE;Should the key be shifted to upper case
- before insertion into the index and before
- index search? (key field in data record is
- not changed)
-
- KeyType := Key_xxxxx; Specify key type... Allowed types are:
-
- Key_Integer - Key is integer, KeyLength is assumed to be 2.
-
- Key_Char - Key is CHAR or ARRAY of CHAR or BYTE; KeyLength
- specifies the number of bytes in the array.
-
- Key_String - Key is a string; KeyLength is ignored.
-
- Key_Real - Key is a REAL number; converted to a 16 byte
- string for the index; KeyLength is ignored.
- (Note.. I have yet to find a use for Real
- number keys)
- END;
- WITH Key [2] DO BEGIN... Etc for each key
- END;
-
- Also defined in File_Type is IOError: BOOLEAN which should be checked
- after each call to a MULTIKEY routine. The meaning of IOError varies
- with the routine.
-
- Anyname_Rec: Your_Record_Description; Must contain a "Status"
- INTEGER field at the very beginning of the record as recommended
- in the Turbo Database Toolbox refrence manual. The first two
- bytes of every record are FORCED to binary zeros on each Add
- or Update operation and may not be part of a key field.
-
- Routines:
-
- PROCEDURE Open_File (VAR F: File_Type);
-
- Opens the data/index files; Rebuilds missing index files;
- Rebuilds freespace chain while it's at it if any index
- files were missing.
-
- (The rebuild features were incorporated for an application
- running on a CP/M machine 40 miles away supported by
- clerical personnel and me by long-distance telephone. If
- the system crashed, it was OK in this case to lose a little
- data as long as the application could be run to check/re-do
- updates. It is quick and relatively safe to tell the clerk
- over the phone how to delete the index files, then when she
- fires up the application, rebuild guarantees the
- consistancy and viability of the database. There is no
- guarantee here however of cross-file consistancey. If your
- application involves multiple files, that will have to be
- checked at the application level)
-
- F.IOError will be TRUE iff, while rebuilding an index file,
- a duplicate key was encountered which prevented a key from
- being added normally. This usually only happens where a
- program change has added or altered a key field
- description and existing data violates the new Unique
- attribute (in other words.. programmer error).
-
- PROCEDURE Read_Record (VAR F: File_Type; K: INTEGER; VAR Rec);
-
- Read a record from file. Key number is specified by K.
- Search key should be placed in the record at the proper
- offset prior to calling Read_Record. Index file K is
- searched for a match to search key or next higher key if no
- match found. If search key is higher than any on file,
- F.IOError is set TRUE and Rec remains unchanged. This
- routine sets the beginning record for subsequent
- Next_Record, Previous_Record calls for the same K.
-
- PROCEDURE Next_Record (VAR F: File_Type; K: INTEGER; VAR Rec);
-
- Read the next record by key K from the file. At EOF,
- F.IOError is set TRUE and the contents of Rec are unchanged.
-
- PROCEDURE Previous_Record (VAR F: File_Type; K: INTEGER; VAR Rec);
-
- Read the previous record by key K from the file. At BOF,
- F.IOError is set TRUE and the contents of Rec are unchanged.
-
- PROCEDURE Add_Record (VAR F: File_Type; VAR Rec);
-
- Add record Rec to the file. Insert all keys in index files.
- IOError set TRUE if duplicate key on a Unique key index.
- If IOError then record is not added and any key insertions
- made before discovery of duplicate key are backed out.
-
- PROCEDURE Update_Record (VAR F: File_Type; VAR Rec);
-
- Update last record retrieved or added. Delete and re-insert
- any key fields which may have changed. You must have done a
- valid (no IOError) retrieval, Add, or Update operation immediately
- before calling Update_Record and you should check to be certain
- you received the record you were expecting. See sample program
- for details. IOError set TRUE if any key field was changed
- to a duplicate key on a Unique index. If IOError then record
- is not updated and any key changes are backed out.
-
- PROCEDURE Delete_Record (VAR F: File_Type);
-
- Delete last record retrieved, updated, or added. Delete any
- key fields which may have changed. You must have done a valid
- (no IOError) retrieval, Add, or Update operation immediately
- before calling Delete_Record and you should check to be certain
- you received the record you were expecting. See sample program
- for details. IOError set TRUE if no valid record retrieval done
- before Delete_Record called.
-
- PROCEDURE Close_File (Var Anyname_File: File_Type);
-
- Obvious. IOError never set.
-
- FUNCTION KeyOffset (VAR Rec; VAR Field): INTEGER;
-
- Pass it a record and a field within that record (untyped parameters)
- and KeyOffset returns the offset in bytes from the record to the field.
- This function may be used to tie the key description set-up as
- described above to the record description and thereby save you
- much frustration should the record layout change and you forget to
- change the key description.
-
- Five things to check when adding a new key field to your program:
-
- 1. Check MaxKeyLen to be sure your new key will fit.
-
- 2. Check MaxKeys to be sure the key table in File_Type is big enough for
- your new key ($R+ will catch this I think).
-
- 3. Change Number_Of_Keys in the file description set-up (Be sure to do
- this if you _Remove_ a key too).
-
- 4. Build a new key description for your new key in the file description
- set-up (WITH Key [nn] DO BEGIN...) making sure the offset is at
- least 2.
-
- 5. Use the new key for something (Why did you create it?).
-
- 8 Bit / 16 Bit changes:
-
- The program is set up for 16 bit system use, but it was developed
- on an 8 bit system. There are two changes to be made near the top
- of the file for 8 bit operation. They are marked with the
- string "8 Bit" for which you may ^QF if you can fit this file in
- your 8 bit editor. The changes involve moving comment brackets
- around and should be obvious. If you cannot fit this file in your
- editor and have no other editor, or the changes are not obvious,
- you may insert the following short routine in your program before
- the {$I MULTIKEY.BOX} Include to allow the program to compile:
-
- FUNCTION Ofs (VAR x): INTEGER; BEGIN
- Ofs := ADDR (x);
- END;
-
- Any comments or improvements would be welcome. Address EasyPlex or
- Borland SIG correspondence to: Edwin Floyd 76067,747. Please claim
- credit for any modifications in the update history below.
-
- Update History
- Date Who Change
- -------- ------------------------ --------------------------------------------
- 03/26/86 Edwin Floyd First release, v1.0
-
- ------------------------------------------------------------------------------
-
- The following program is submitted as an example. Block-mark the
- program and write it to a file; Compile and run. Wipe out the index
- files (DEL CLASS.IX* or ERA CLASS.IX*.) and run again; note that the
- index files are rebuilt. Add a new key field: Location (^QF Search
- for "{@" for suggested changes); re-compile/run program; note that new
- index file is built; note ease of adding key fields.
-
- {Beginning of Example Program}
-
- PROGRAM MultiKey_Example;
- {$R+}
- CONST
- MaxDataRecSize = 150;
- MaxKeylen = 30; {@ Check to add new key; will it fit? Yes.}
- PageSize = 16;
- Order = 8;
- PageStackSize = 8;
- MaxHeight = 7;
-
- MaxKeys = 3; {Must appear before $I MULTIKEY.BOX}
- {@MaxKeys = 4; Change to add new key}
-
- {$I ACCESS.BOX }
- {$I GETKEY.BOX }
- {$I ADDKEY.BOX }
- {$I DELKEY.BOX }
-
- {$I MULTIKEY.BOX } {<-- This is it!}
-
- TYPE
- NameType = ARRAY [1..25] OF CHAR;
- Class_Record = RECORD {Record for sample application}
- Status: INTEGER; {Always allocate the first 2 bytes; Never touch}
- Id: INTEGER; {Primary Key}
- Title: NameType; {Secondary Key}
- Instructor: NameType; {Tertiary Key}
- Location: NameType; {New Key, add later}
- Registered: INTEGER;
- END;
-
- VAR
- Rec: Class_Record;
- Indexed_File: File_Type; {Defined in MULTIKEY.BOX}
-
- PROCEDURE Display_Record (VAR R: Class_Record); BEGIN
- WITH R DO BEGIN
- WRITELN (' Id: ', Id, ', Registered: ', Registered, ', Title: ', Title);
- WRITELN (' Instructor: ', Instructor);
- WRITELN (' Location: ', Location);
- WRITELN;
- END;
- END;
-
- PROCEDURE Pause;
- VAR
- Ch: Char;
- BEGIN
- WRITELN;
- WRITE ('Press any key to continue...');
- READ (KBD, Ch);
- WRITELN
- END;
-
- BEGIN {MultiKey_Example}
-
- InitIndex;
-
- WITH Indexed_File DO BEGIN
- Name := 'CLASS';
- RecSize := Sizeof (Rec);
- Number_Of_Keys := 3;
- {@Number_Of_Keys := 4; Change to add new key}
- WITH Key [1] DO BEGIN
- Offset := KeyOffset (Rec, Rec.Id);
- KeyLength := Sizeof (Rec.Id);
- Extension := 'IX1';
- Unique := TRUE; {No duplicates allowed}
- Upshift := FALSE;
- KeyType := Key_Integer;
- END;
- WITH Key [2] DO BEGIN
- Offset := KeyOffset (Rec, Rec.Title);
- KeyLength := Sizeof (Rec.Title);
- Extension := 'IX2';
- Unique := FALSE;
- Upshift := TRUE;
- KeyType := Key_Char;
- END;
- WITH Key [3] DO BEGIN
- Offset := KeyOffset (Rec, Rec.Instructor);
- KeyLength := Sizeof (Rec.Instructor);
- Extension := 'IX3';
- Unique := FALSE;
- Upshift := TRUE;
- KeyType := Key_Char;
- END;
- {@ Delete this line to add new key
- WITH Key [4] DO BEGIN
- Offset := KeyOffset (Rec, Rec.Location);
- KeyLength := Sizeof (Rec.Location);
- Extension := 'IX4';
- Unique := FALSE;
- Upshift := TRUE;
- KeyType := Key_Char;
- END;
- Delete this line to add new key}
- END;
-
- Open_File (Indexed_File);
- IF Indexed_File.IOError THEN BEGIN
- WRITELN ('Error rebuilding index file, duplicate key.');
- WRITELN ('Continuing anyway just to see what it does.');
- END;
-
- WITH Rec DO BEGIN
-
- {Dummy up some data for demonstration}
- WRITELN;
- WRITELN ('Inserting records into database');
- WRITELN ('On first run, add for Record 3 will fail (duplicate key)');
- WRITELN ('On subsequent runs, three or four adds will fail');
-
- Id := 1;
- Title := 'Marking Time for Managers';
- Instructor := 'White, Perry ';
- Location := 'Daily Globe Building ';
- Registered := 10;
- Add_Record (Indexed_File, Rec);
- IF Indexed_File.IOError THEN WRITELN ('Record ', Id, ' not added');
-
- Id := 2;
- Title := 'Evading FAA Regulations ';
- Instructor := 'Yeager, Chuck ';
- Location := 'Nevada Desert ';
- Registered := 20;
- Add_Record (Indexed_File, Rec);
- IF Indexed_File.IOError THEN WRITELN ('Record ', Id, ' not added');
-
- Id := 3;
- Title := 'Elementary Wall Crawling ';
- Instructor := 'Parker, Peter ';
- Location := 'Apt 3B ';
- Registered := 30;
- Add_Record (Indexed_File, Rec);
- IF Indexed_File.IOError THEN WRITELN ('Record ', Id, ' not added');
-
- Id := 3;
- Title := 'This record has the same ';
- Instructor := 'primary key as the previo';
- Location := 'us one. Add will fail. ';
- Registered := 35;
- Add_Record (Indexed_File, Rec);
- IF Indexed_File.IOError THEN WRITELN ('Record ', Id, ' not added');
-
- Id := 4;
- Title := 'Stalls, Spins, Wreckage ';
- Instructor := 'Yeager, Chuck '; {Duplicate OK}
- Location := 'All over Nevada ';
- Registered := 40;
- Add_Record (Indexed_File, Rec);
- IF Indexed_File.IOError THEN WRITELN ('Record ', Id, ' not added');
- Pause;
-
- {Now read them back in various ways}
-
- WRITELN ('Reading forward by primary key: Id');
- WRITELN ('Four records, first time thru');
- WRITELN;
- Id := 0;
- Read_Record (Indexed_File, 1, Rec);
- WHILE NOT Indexed_File.IOError DO BEGIN
- Display_Record (Rec);
- Next_Record (Indexed_File, 1, Rec);
- END;
- Pause;
-
- WRITELN ('Reading forward by secondary key: Title');
- WRITELN;
- Title := ' ';
- Read_Record (Indexed_File, 2, Rec);
- WHILE NOT Indexed_File.IOError DO BEGIN
- Display_Record (Rec);
- Next_Record (Indexed_File, 2, Rec);
- END;
- Pause;
-
- WRITELN ('Reading backward by secondary key: Title');
- WRITELN;
- Title := ' ';
- Read_Record (Indexed_File, 2, Rec);
- Previous_Record (Indexed_File, 2, Rec); {Get to BOF}
- REPEAT
- Previous_Record (Indexed_File, 2, Rec);
- IF NOT Indexed_File.IOError THEN Display_Record (Rec);
- UNTIL Indexed_File.IOError;
- Pause;
-
- WRITELN ('Locate by partial tertiary key: Instructor = "YEAG "');
- WRITELN ('And forward from there to EOF by key: Instructor');
- WRITELN;
- Instructor := 'YEAG '; {Note upper case search}
- Read_Record (Indexed_File, 3, Rec);
- WHILE NOT Indexed_File.IOError DO BEGIN
- Display_Record (Rec);
- Next_Record (Indexed_File, 3, Rec);
- END;
- Pause;
-
- WRITELN ('Locate, Update - Record Id = 4');
- WRITELN ('Update will fail (duplicate key) second time through');
- Id := 4;
- Read_Record (Indexed_File, 1, Rec);
- IF (Id = 4) AND NOT Indexed_File.IOError THEN BEGIN
- WRITELN (' Locate OK');
- Id := 0; {OK to change key}
- Title := 'Luck in Aviation '; {""}
- Instructor := 'Yeager, Chuck ';
- Location := 'This record updated ';
- Registered := 60;
- Writeln (' Record changed...');
- Display_Record (Rec);
- Update_Record (Indexed_File, Rec);
- IF Indexed_File.IOError THEN WRITELN ('Record ', Id, ' not updated')
- ELSE WRITELN (' Update successful');
- END;
- Pause;
-
- WRITELN ('Locate, Delete - Record Id = 3');
- Id := 3;
- Read_Record (Indexed_File, 1, Rec);
- IF (Id = 3) AND NOT Indexed_File.IOError THEN BEGIN
- WRITELN (' Locate OK');
- delete_Record (Indexed_File);
- IF Indexed_File.IOError THEN WRITELN ('Record ', Id, ' not deleted')
- ELSE WRITELN (' Delete Successful');
-
- {Note... At this point, the record just deleted remains unchanged
- in Rec and may be immediately added back to the database}
-
- END;
- Pause;
-
- WRITELN ('Reading forward by primary key again');
- Id := 0;
- Read_Record (Indexed_File, 1, Rec);
- WHILE NOT Indexed_File.IOError DO BEGIN
- Display_Record (Rec);
- Next_Record (Indexed_File, 1, Rec);
- END;
- Pause;
-
- {@ Delete this line to read by new key
- WRITELN ('Reading forward by new key');
- Location := ' ';
- Read_Record (Indexed_File, 4, Rec);
- WHILE NOT Indexed_File.IOError DO BEGIN
- Display_Record (Rec);
- Next_Record (Indexed_File, 4, Rec);
- END;
- Pause;
- Delete this line to read by new key}
- END;
-
- Close_File (Indexed_File);
- WRITELN ('End of Demo');
- END.
-
- {End of Example Program}
-
- *)
-
- {Beginning of MULTIKEY routines}
-
- TYPE
- KEY_TYPE = (KEY_INTEGER, KEY_CHAR, KEY_STRING, KEY_REAL);
- FILE_TYPE = RECORD {Fields marked with * must be set before OPEN_FILE}
- NAME: {8 Bit.. STRING [10];}{* Data/Index file [drive:\path and] base}
- {16 Bit.} STRING [60]; { name; extension will be .DAT for data
- file or EXTENSION (see below) for
- index.}
- RECSIZE: INTEGER; {* Length of records in file;
- use RECSIZE := SIZEOF (rec)}
- IOERROR: BOOLEAN; { Test after each call; True if error}
- REC_REF: INTEGER; { Record reference in data file}
- DATA_FILE: DATAFILE; { Data file control block}
- NUMBER_OF_KEYS: BYTE; {* Counter for array below}
- KEY: ARRAY [1..MAXKEYS] OF RECORD {Each key must be explicitly described}
- OFFSET: INTEGER; {* Offset from start of record to this key;
- use OFFSET := KEYOFFSET (rec, field)}
- KEYLENGTH: BYTE; {* Length of this key;
- use KEYLENGTH := SIZEOF (field)}
- EXTENSION: STRING [3]; {* Extension for this index file name; the
- [drive:\path and] name is from NAME
- above}
- UNIQUE: BOOLEAN; {* True if this key is to be unique}
- UPSHIFT: BOOLEAN; {* True if case to be ignored on index}
- KEYTYPE: KEY_TYPE; {* Data type of key}
- INDEX_FILE: INDEXFILE; { Index file control block}
- END;
- END;
-
- VAR
- WORK_KEY: TaKeyStr; {Key work area}
- WORK_REC: ARRAY [0..MAXDATARECSIZE] OF CHAR;
- {Record work area}
-
- FUNCTION KEYOFFSET (VAR R; VAR F): INTEGER; BEGIN
- {Use to compute the OFFSET parameter of a key}
- KEYOFFSET :=
- {8 Bit.. (ADDR (F) + $8000) - (ADDR (R) + $8000); }
- {16 Bit.} (OFS (F) + $8000) - (OFS (R) + $8000);
- END;
-
- PROCEDURE KEY_TO_STRING (VAR KEY; LEN: BYTE; TYP: KEY_TYPE; UP: BOOLEAN);
- {Converts a key of the designated type to a string in WORK_KEY for Turbo
- Index storage}
- VAR
- INTEGER_KEY: INTEGER ABSOLUTE KEY;
- CHAR_KEY: ARRAY [1..MAXKEYLEN] OF CHAR ABSOLUTE KEY;
- STRING_KEY: STRING [MAXKEYLEN] ABSOLUTE KEY;
- REAL_KEY: REAL ABSOLUTE KEY;
- I: INTEGER;
- BEGIN
- CASE TYP OF
- KEY_INTEGER: BEGIN
- I := INTEGER_KEY + $8000;
- WORK_KEY := CHR (HI (I)) + CHR (LO (I));
- END;
- KEY_CHAR: BEGIN
- IF LEN > MAXKEYLEN THEN LEN := MAXKEYLEN;
- WORK_KEY [0] := CHR (LEN);
- IF LEN > 0 THEN MOVE (KEY, WORK_KEY [1], LEN);
- END;
- KEY_STRING: WORK_KEY := STRING_KEY;
- KEY_REAL: STR (REAL_KEY:16, WORK_KEY);
- END;
- IF UP AND ((TYP = KEY_CHAR) OR (TYP = KEY_STRING)) THEN
- FOR I := 1 TO LENGTH (WORK_KEY) DO
- WORK_KEY [I] := UPCASE (WORK_KEY [I]);
- END;
-
- PROCEDURE CLOSE_FILE (VAR F: FILE_TYPE);
- {Close database and all index files}
- VAR
- I: INTEGER;
- BEGIN
- WITH F DO BEGIN
- CLOSEFILE (DATA_FILE);
- FOR I := 1 TO NUMBER_OF_KEYS DO BEGIN
- WITH KEY [I] DO CLOSEINDEX (INDEX_FILE);
- END;
- END;
- END;
-
- PROCEDURE OPEN_FILE (VAR F: FILE_TYPE);
- {Opens a multi-key database and all index files, re-builds missing index
- file(s) and database freespace chain}
- VAR
- I, DUP: INTEGER;
- FLAG: INTEGER ABSOLUTE WORK_REC;
- KEY_FILE_OK: ARRAY [1..MAXKEYS] OF BOOLEAN;
- ALL_KEYS_OK: BOOLEAN;
- BEGIN
- WITH F DO BEGIN
- IF (NUMBER_OF_KEYS < 1) OR (NUMBER_OF_KEYS > MAXKEYS) THEN BEGIN
- WRITELN ('In file ', NAME, ', ', NUMBER_OF_KEYS,
- ' keys specified, 1..', MAXKEYS, ' keys allowed');
- HALT;
- END;
- ALL_KEYS_OK := TRUE;
- IOERROR := FALSE;
- OPENFILE (DATA_FILE, NAME + '.DAT', RECSIZE);
- IF NOT OK THEN BEGIN
- MAKEFILE (DATA_FILE, NAME + '.DAT', RECSIZE);
- FOR I := 1 TO NUMBER_OF_KEYS DO BEGIN
- WITH KEY [I] DO BEGIN
- IF UNIQUE THEN DUP := 0 ELSE DUP := 1;
- MAKEINDEX (INDEX_FILE, NAME + '.' + EXTENSION, KEYLENGTH, DUP);
- CLEARKEY (INDEX_FILE);
- END;
- END;
- END ELSE BEGIN
- FOR I := 1 TO NUMBER_OF_KEYS DO BEGIN
- WITH KEY [I] DO BEGIN
- IF OFFSET < 2 THEN BEGIN
- WRITELN ('Key Offset for key ', I, ' is ', OFFSET,
- ', Minimum is 2 for file ', NAME);
- HALT;
- END;
- IF (KEYTYPE = KEY_CHAR)
- AND ((KEYLENGTH < 1) OR (KEYLENGTH > MAXKEYLEN)) THEN BEGIN
- WRITELN ('KeyLength for key ', I, ' is ', KEYLENGTH,
- ', it must be between 1 and ', MAXKEYLEN, ' in file ',
- NAME);
- HALT;
- END;
- IF UNIQUE THEN DUP := 0 ELSE DUP := 1;
- OPENINDEX (INDEX_FILE, NAME + '.' + EXTENSION, KEYLENGTH, DUP);
- IF NOT OK THEN BEGIN
- MAKEINDEX (INDEX_FILE, NAME + '.' + EXTENSION, KEYLENGTH,
- DUP);
- ALL_KEYS_OK := FALSE;
- KEY_FILE_OK [I] := FALSE;
- END ELSE KEY_FILE_OK [I] := TRUE;
- CLEARKEY (INDEX_FILE);
- END;
- END;
- END;
- IF NOT ALL_KEYS_OK THEN BEGIN
- WRITELN ('Please wait, rebuilding index file(s) in ', NAME, ' for ',
- FILELEN (DATA_FILE), ' records');
- REC_REF := 1;
- WITH DATA_FILE DO BEGIN
- FirstFree := -1;
- NumberFree := 0;
- END;
- WHILE REC_REF < FILELEN (DATA_FILE) DO BEGIN
- GETREC (DATA_FILE, REC_REF, WORK_REC);
- IF FLAG = 0 THEN BEGIN
- FOR I := 1 TO NUMBER_OF_KEYS DO
- IF NOT KEY_FILE_OK [I] THEN WITH KEY [I] DO BEGIN
- KEY_TO_STRING (WORK_REC [OFFSET], KEYLENGTH, KEYTYPE,
- UPSHIFT);
- ADDKEY (INDEX_FILE, REC_REF, WORK_KEY);
- IF NOT OK THEN IOERROR := TRUE;
- END;
- END ELSE BEGIN
- WITH DATA_FILE DO BEGIN
- IF FLAG <> FirstFree THEN BEGIN
- FLAG := FirstFree;
- PUTREC (DATA_FILE, REC_REF, WORK_REC);
- FirstFree := REC_REF;
- END;
- NumberFree := SUCC (NumberFree);
- END;
- END;
- REC_REF := SUCC (REC_REF);
- END;
- END;
- REC_REF := 0;
- END;
- END;
-
- PROCEDURE DELETE_RECORD (VAR F: FILE_TYPE);
- {Delete the last record retrieved from the database and all its keys.
- IOERROR indicates no valid last record retrieved.}
- VAR
- K: INTEGER;
- BEGIN
- WITH F DO IF REC_REF <> 0 THEN BEGIN
- GETREC (DATA_FILE, REC_REF, WORK_REC);
- DELETEREC (DATA_FILE, REC_REF);
- FOR K := 1 TO NUMBER_OF_KEYS DO WITH KEY [K] DO BEGIN
- KEY_TO_STRING (WORK_REC [OFFSET], KEYLENGTH, KEYTYPE, UPSHIFT);
- DELETEKEY (INDEX_FILE, REC_REF, WORK_KEY);
- END;
- IOERROR := FALSE;
- REC_REF := 0;
- END ELSE IOERROR := TRUE;
- END;
-
- PROCEDURE READ_RECORD (VAR F: FILE_TYPE; K: INTEGER; VAR R);
- {Read a record from the database with a key equal to or higher than that
- indicated by key field K in record R. IOERROR indicates search key was
- higher than any in the index.}
- VAR
- REC: ARRAY [0..MAXDATARECSIZE] OF CHAR ABSOLUTE R;
- REF: INTEGER;
- BEGIN
- WITH F DO BEGIN
- IF (K > NUMBER_OF_KEYS) OR (K < 1) THEN BEGIN
- WRITELN ('Key ', K, ' Referenced, Keys 1..', NUMBER_OF_KEYS,
- ' Defined in file ', NAME);
- HALT;
- END;
- WITH KEY [K] DO BEGIN
- KEY_TO_STRING (REC [OFFSET], KEYLENGTH, KEYTYPE, UPSHIFT);
- SEARCHKEY (INDEX_FILE, REF, WORK_KEY);
- IF OK THEN GETREC (DATA_FILE, REF, REC);
- IF OK THEN REC_REF := REF;
- IOERROR := NOT OK;
- END;
- END;
- END;
-
- PROCEDURE NEXT_RECORD (VAR F: FILE_TYPE; K: INTEGER; VAR R);
- {Read the next record by key K from the database. IOERROR indicates end of
- file by key K.}
- VAR
- REC: ARRAY [0..MAXDATARECSIZE] OF CHAR ABSOLUTE R;
- REF: INTEGER;
- BEGIN
- WITH F DO BEGIN
- IF (K > NUMBER_OF_KEYS) OR (K < 1) THEN BEGIN
- WRITELN ('Key ', K, ' Referenced, Keys 1..', NUMBER_OF_KEYS,
- ' Defined in file ', NAME);
- HALT;
- END;
- WITH KEY [K] DO BEGIN
- NEXTKEY (INDEX_FILE, REF, WORK_KEY);
- IF OK THEN GETREC (DATA_FILE, REF, REC);
- IF OK THEN REC_REF := REF;
- IOERROR := NOT OK;
- END;
- END;
- END;
-
- PROCEDURE PREVIOUS_RECORD (VAR F: FILE_TYPE; K: INTEGER; VAR R);
- {Read the previous record by key K from the database. IOERROR indicates start
- of file by key K.}
- VAR
- REC: ARRAY [0..MAXDATARECSIZE] OF CHAR ABSOLUTE R;
- REF: INTEGER;
- BEGIN
- WITH F DO BEGIN
- IF (K > NUMBER_OF_KEYS) OR (K < 1) THEN BEGIN
- WRITELN ('Key ', K, ' Referenced, Keys 1..', NUMBER_OF_KEYS,
- ' Defined in file', NAME);
- HALT;
- END;
- WITH KEY [K] DO BEGIN
- PREVKEY (INDEX_FILE, REF, WORK_KEY);
- IF OK THEN GETREC (DATA_FILE, REF, REC);
- IF OK THEN REC_REF := REF;
- IOERROR := NOT OK;
- END;
- END;
- END;
-
- PROCEDURE ADD_RECORD (VAR F: FILE_TYPE; VAR R);
- {
- Add record R to the database and update all index files. IOERROR usually
- indicates a duplicate key in a unique key index.
- }
- VAR
- REC: ARRAY [0..MAXDATARECSIZE] OF CHAR ABSOLUTE R;
- FLAG: INTEGER ABSOLUTE R;
- REF, K: INTEGER;
- BEGIN
- WITH F DO BEGIN
- IOERROR := FALSE;
- FLAG := 0;
- ADDREC (DATA_FILE, REF, REC);
- K := 1;
- WHILE (K <= NUMBER_OF_KEYS) AND NOT IOERROR DO BEGIN
- WITH KEY [K] DO BEGIN
- KEY_TO_STRING (REC [OFFSET], KEYLENGTH, KEYTYPE, UPSHIFT);
- ADDKEY (INDEX_FILE, REF, WORK_KEY);
- IOERROR := NOT OK;
- END;
- K := SUCC (K);
- END;
- IF IOERROR THEN BEGIN
- K := PRED (PRED (K));
- WHILE K > 0 DO BEGIN
- WITH KEY [K] DO BEGIN
- KEY_TO_STRING (REC [OFFSET], KEYLENGTH, KEYTYPE, UPSHIFT);
- DELETEKEY (INDEX_FILE, REF, WORK_KEY);
- END;
- K := PRED (K);
- END;
- DELETEREC (DATA_FILE, REF);
- END ELSE REC_REF := REF;
- END;
- END;
-
- PROCEDURE UPDATE_RECORD (VAR F: FILE_TYPE; VAR R);
- {
- Update the last retrieved record with data from record R and update any
- index files whose keys were changed. IOERROR usually indicates a duplicate
- key in a unique key index.
- }
- VAR
- REC: ARRAY [0..MAXDATARECSIZE] OF CHAR ABSOLUTE R;
- FLAG: INTEGER ABSOLUTE R;
- S: STRING [MAXKEYLEN];
- K: INTEGER;
- BEGIN
- WITH F DO BEGIN
- IOERROR := FALSE;
- IF REC_REF <> 0 THEN BEGIN
- FLAG := 0;
- GETREC (DATA_FILE, REC_REF, WORK_REC);
- K := 1;
- WHILE (K <= NUMBER_OF_KEYS) AND NOT IOERROR DO BEGIN
- WITH KEY [K] DO BEGIN
- KEY_TO_STRING (WORK_REC [OFFSET], KEYLENGTH, KEYTYPE, UPSHIFT);
- S := WORK_KEY;
- KEY_TO_STRING (REC [OFFSET], KEYLENGTH, KEYTYPE, UPSHIFT);
- IF S <> WORK_KEY THEN BEGIN
- DELETEKEY (INDEX_FILE, REC_REF, S);
- ADDKEY (INDEX_FILE, REC_REF, WORK_KEY);
- IOERROR := NOT OK;
- IF IOERROR THEN ADDKEY (INDEX_FILE, REC_REF, S);
- END;
- K := SUCC (K);
- END;
- END;
- IF IOERROR THEN BEGIN
- K := PRED (PRED (K));
- WHILE K > 0 DO BEGIN
- WITH KEY [K] DO BEGIN
- KEY_TO_STRING (WORK_REC [OFFSET], KEYLENGTH, KEYTYPE,
- UPSHIFT);
- S := WORK_KEY;
- KEY_TO_STRING (REC [OFFSET], KEYLENGTH, KEYTYPE, UPSHIFT);
- IF S <> WORK_KEY THEN BEGIN
- DELETEKEY (INDEX_FILE, REC_REF, WORK_KEY);
- ADDKEY (INDEX_FILE, REC_REF, S);
- END;
- END;
- K := PRED (K);
- END;
- END ELSE PUTREC (DATA_FILE, REC_REF, REC);
- END ELSE IOERROR := TRUE;
- END;
- END;
-
- {End of MULTIKEY routines}