home *** CD-ROM | disk | FTP | other *** search
- Mulkey4 20 April 1988
-
- James C Walker <Cap'n> 72355,1616
-
-
- About a year ago I started programming with the
- Database Toolbox. At that time there was no TAHIGH,
- just the low level direct calls. Then Edwin Floyd
- created MULKEY.
-
- Mulkey4 is an enhanced 'child' of that version. Its
- specialty is to offer a high-level shell for the
- Database Toolbox. It will automate many of the lower
- level processes.
-
- I have included a section from MULKEY.BOX in DL 4 for
- reference. Though, the following observations should
- be made:
-
- Now the Database Toolbox requires you to create
- TACCESS.TPU. To do this you should run
- "TABUILD PROGNAME /h- /w+ ". This will allow you to
- adjust the constants [page size, etc..] for your
- application.
-
- Your Uses Clause should be USE TACCESS, MULKEY4;
-
- I have included an example that shows how this is now
- done. It also shows how to handle a relational
- database situation.
-
- With Edwin's permission, I have uploaded MULKEY4 for
- your enjoyment. If you have any questions, criticisms
- or suggestions, PLEASE contact me.
-
- James
-
- ************************Original Documentation*****************
-
- (*
- 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; *** no longer used ***
-
- 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} *** no longer used ***
-
- 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} *** older example left in for illustration ***
-
- 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 } *** these are the old Dbox routines ***
- {$I GETKEY.BOX } *** now use USE TACCESS; ***
- {$I ADDKEY.BOX }
- {$I DELKEY.BOX }
-
- {$I MULTIKEY.BOX } {<-- This is it!} *** Now use USE MULKEY4; ***
-
- 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}
-
- *)