home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / MKEY4.ZIP / MKEY4.DOC next >
Encoding:
Text File  |  1988-04-20  |  20.3 KB  |  538 lines

  1.                            Mulkey4                              20 April 1988
  2.  
  3.      James C Walker <Cap'n> 72355,1616
  4.  
  5.  
  6.      About a year ago I started programming with the
  7.      Database Toolbox. At that time there was no TAHIGH,
  8.      just the low level direct calls. Then Edwin Floyd
  9.      created MULKEY.
  10.  
  11.      Mulkey4 is an enhanced 'child' of that version. Its
  12.      specialty is to offer a high-level shell for the
  13.      Database Toolbox. It will automate many of the lower
  14.      level processes.
  15.  
  16.      I have included a section from MULKEY.BOX in DL 4 for
  17.      reference. Though, the following observations should
  18.      be made:
  19.  
  20.      Now the Database Toolbox requires you to create
  21.      TACCESS.TPU. To do this you should run
  22.      "TABUILD PROGNAME /h- /w+ ". This will allow you to
  23.      adjust the constants [page size, etc..] for your
  24.      application.
  25.  
  26.      Your Uses Clause should be USE TACCESS, MULKEY4;
  27.  
  28.      I have included an example that shows how this is now
  29.      done. It also shows how to handle a relational
  30.      database situation.
  31.  
  32.      With Edwin's permission, I have uploaded MULKEY4 for
  33.      your enjoyment. If you have any questions, criticisms
  34.      or suggestions, PLEASE contact me.
  35.  
  36.      James
  37.  
  38. ************************Original Documentation*****************
  39.  
  40. (*
  41. MULTIKEY v 1.0 - Routines to help you use Turbo Database Toolbox
  42.                  Edwin Floyd 76067,747
  43.  
  44. These routines provide three services to the programmer:
  45.  
  46.    1. Automate the creation and maintenance of multiple index files within
  47.       a Database Toolbox application.
  48.  
  49.    2. Automatically rebuild a lost/corrupted (or build a new) index file.
  50.  
  51.    3. Rebuild the freespace chain on a corrupted data file.
  52.  
  53. Under the premise that an example is worth a basketfull of exposition, a
  54. brief description of the routines and data structures is provided followed
  55. by an example program.
  56.  
  57.    CONST MaxKeys        = nn;   *** no longer used ***
  58.  
  59. Must appear before the include for MULTIKEY.BOX.  This specifies the
  60. maximum number of keys to be defined for any record in this program.
  61.  
  62.    {$I MULTIKEY.BOX}  *** no longer used ***
  63.  
  64. Must appear after all Database Toolbox includes.  All Toolbox routines
  65. are referenced, so don't leave any Includes out.
  66.  
  67.    Anyname_File, ...: File_Type;
  68.  
  69. File_Type is defined in MULTIKEY.BOX.  For each File_Type record, you
  70. must fill out a description of the file and all key fields in the record
  71. sometime before calling Open_File:
  72.  
  73.    WITH Anyname_File DO BEGIN
  74.       Name := 'xxxxxxxx';        You may specify the Drive, path and file name,
  75.                                  Leave off the extension, the data file will
  76.                                  have the extension ".DAT" and you may specify
  77.                                  the extension of each index file below.
  78.  
  79.       RecSize := Sizeof (Rec);   Specify the size of the data record
  80.  
  81.       Number_Of_Keys := nn;      Specify the number of keys (<= MaxKeys) for
  82.                                  This file.
  83.  
  84.       WITH Key [1] DO BEGIN      Describe each key...
  85.  
  86.          Offset    := KeyOffset (Rec, Rec.Key);
  87.                                  Specify offset of first byte of key from
  88.                                  beginning of record (first byte of record
  89.                                  is offset zero (0) but the minimum key
  90.                                  offset allowed is 2 because of the record
  91.                                  status word).  You may use the KeyOffset
  92.                                  function described below.
  93.  
  94.          KeyLength := Sizeof (Rec.Key);
  95.                                  Specify the length in bytes of the key for
  96.                                  CHAR or ARRAY of CHAR key.
  97.  
  98.          Extension := 'iii';     Specify the extension for the index file
  99.  
  100.          Unique    := TRUE/FALSE;Is this a unique key (TRUE) or are duplicates
  101.                                  allowed (FALSE)?
  102.  
  103.          Upshift   := TRUE/FALSE;Should the key be shifted to upper case
  104.                                  before insertion into the index and before
  105.                                  index search? (key field in data record is
  106.                                  not changed)
  107.  
  108.          KeyType   := Key_xxxxx; Specify key type... Allowed types are:
  109.  
  110.             Key_Integer - Key is integer, KeyLength is assumed to be 2.
  111.  
  112.             Key_Char    - Key is CHAR or ARRAY of CHAR or BYTE; KeyLength
  113.                           specifies the number of bytes in the array.
  114.  
  115.             Key_String  - Key is a string; KeyLength is ignored.
  116.  
  117.             Key_Real    - Key is a REAL number; converted to a 16 byte
  118.                           string for the index; KeyLength is ignored.
  119.                           (Note.. I have yet to find a use for Real
  120.                           number keys)
  121.       END;
  122.       WITH Key [2] DO BEGIN... Etc for each key
  123.    END;
  124.  
  125. Also defined in File_Type is IOError: BOOLEAN which should be checked
  126. after each call to a MULTIKEY routine.  The meaning of IOError varies
  127. with the routine.
  128.  
  129.    Anyname_Rec: Your_Record_Description;  Must contain a "Status"
  130.       INTEGER field at the very beginning of the record as recommended
  131.       in the Turbo Database Toolbox refrence manual.  The first two
  132.       bytes of every record are FORCED to binary zeros on each Add
  133.       or Update operation and may not be part of a key field.
  134.  
  135. Routines:
  136.  
  137.    PROCEDURE Open_File (VAR F: File_Type);
  138.  
  139.       Opens the data/index files; Rebuilds missing index files;
  140.       Rebuilds freespace chain while it's at it if any index
  141.       files were missing.
  142.  
  143.       (The rebuild features were incorporated for an application
  144.       running on a CP/M machine 40 miles away supported by
  145.       clerical personnel and me by long-distance telephone.  If
  146.       the system crashed, it was OK in this case to lose a little
  147.       data as long as the application could be run to check/re-do
  148.       updates.  It is quick and relatively safe to tell the clerk
  149.       over the phone how to delete the index files, then when she
  150.       fires up the application, rebuild guarantees the
  151.       consistancy and viability of the database.  There is no
  152.       guarantee here however of cross-file consistancey.  If your
  153.       application involves multiple files, that will have to be
  154.       checked at the application level)
  155.  
  156.       F.IOError will be TRUE iff, while rebuilding an index file,
  157.       a duplicate key was encountered which prevented a key from
  158.       being added normally.  This usually only happens where a
  159.       program change has added or altered a key field
  160.       description and existing data violates the new Unique
  161.       attribute (in other words.. programmer error).
  162.  
  163.    PROCEDURE Read_Record (VAR F: File_Type; K: INTEGER; VAR Rec);
  164.  
  165.       Read a record from file.  Key number is specified by K.
  166.       Search key should be placed in the record at the proper
  167.       offset prior to calling Read_Record.  Index file K is
  168.       searched for a match to search key or next higher key if no
  169.       match found.  If search key is higher than any on file,
  170.       F.IOError is set TRUE and Rec remains unchanged.  This
  171.       routine sets the beginning record for subsequent
  172.       Next_Record, Previous_Record calls for the same K.
  173.  
  174.    PROCEDURE Next_Record (VAR F: File_Type; K: INTEGER; VAR Rec);
  175.  
  176.       Read the next record by key K from the file.  At EOF,
  177.       F.IOError is set TRUE and the contents of Rec are unchanged.
  178.  
  179.    PROCEDURE Previous_Record (VAR F: File_Type; K: INTEGER; VAR Rec);
  180.  
  181.       Read the previous record by key K from the file.  At BOF,
  182.       F.IOError is set TRUE and the contents of Rec are unchanged.
  183.  
  184.    PROCEDURE Add_Record (VAR F: File_Type; VAR Rec);
  185.  
  186.       Add record Rec to the file.  Insert all keys in index files.
  187.       IOError set TRUE if duplicate key on a Unique key index.
  188.       If IOError then record is not added and any key insertions
  189.       made before discovery of duplicate key are backed out.
  190.  
  191.    PROCEDURE Update_Record (VAR F: File_Type; VAR Rec);
  192.  
  193.       Update last record retrieved or added.  Delete and re-insert
  194.       any key fields which may have changed.  You must have done a
  195.       valid (no IOError) retrieval, Add, or Update operation immediately
  196.       before calling Update_Record and you should check to be certain
  197.       you received the record you were expecting.  See sample program
  198.       for details.  IOError set TRUE if any key field was changed
  199.       to a duplicate key on a Unique index.  If IOError then record
  200.       is not updated and any key changes are backed out.
  201.  
  202.    PROCEDURE Delete_Record (VAR F: File_Type);
  203.  
  204.       Delete last record retrieved, updated, or added.  Delete any
  205.       key fields which may have changed.  You must have done a valid
  206.       (no IOError) retrieval, Add, or Update operation immediately
  207.       before calling Delete_Record and you should check to be certain
  208.       you received the record you were expecting.  See sample program
  209.       for details.  IOError set TRUE if no valid record retrieval done
  210.       before Delete_Record called.
  211.  
  212.    PROCEDURE Close_File (Var Anyname_File: File_Type);
  213.  
  214.       Obvious. IOError never set.
  215.  
  216.    FUNCTION KeyOffset (VAR Rec; VAR Field): INTEGER;
  217.  
  218.       Pass it a record and a field within that record (untyped parameters)
  219.       and KeyOffset returns the offset in bytes from the record to the field.
  220.       This function may be used to tie the key description set-up as
  221.       described above to the record description and thereby save you
  222.       much frustration should the record layout change and you forget to
  223.       change the key description.
  224.  
  225. Five things to check when adding a new key field to your program:
  226.  
  227.    1. Check MaxKeyLen to be sure your new key will fit.
  228.  
  229.    2. Check MaxKeys to be sure the key table in File_Type is big enough for
  230.       your new key ($R+ will catch this I think).
  231.  
  232.    3. Change Number_Of_Keys in the file description set-up (Be sure to do
  233.       this if you _Remove_ a key too).
  234.  
  235.    4. Build a new key description for your new key in the file description
  236.       set-up (WITH Key [nn] DO BEGIN...) making sure the offset is at
  237.       least 2.
  238.  
  239.    5. Use the new key for something (Why did you create it?).
  240.  
  241. 8 Bit / 16 Bit changes:
  242.  
  243.    The program is set up for 16 bit system use, but it was developed
  244.    on an 8 bit system.  There are two changes to be made near the top
  245.    of the file for 8 bit operation.  They are marked with the
  246.    string "8 Bit" for which you may ^QF if you can fit this file in
  247.    your 8 bit editor.  The changes involve moving comment brackets
  248.    around and should be obvious.  If you cannot fit this file in your
  249.    editor and have no other editor, or the changes are not obvious,
  250.    you may insert the following short routine in your program before
  251.    the {$I MULTIKEY.BOX} Include to allow the program to compile:
  252.  
  253.    FUNCTION Ofs (VAR x): INTEGER; BEGIN
  254.       Ofs := ADDR (x);
  255.    END;
  256.  
  257. Any comments or improvements would be welcome.  Address EasyPlex or
  258. Borland SIG correspondence to: Edwin Floyd 76067,747.  Please claim
  259. credit for any modifications in the update history below.
  260.  
  261. Update History
  262. Date     Who                      Change
  263. -------- ------------------------ --------------------------------------------
  264. 03/26/86 Edwin Floyd              First release, v1.0
  265.  
  266. ------------------------------------------------------------------------------
  267.  
  268. The following program is submitted as an example.  Block-mark the
  269. program and write it to a file; Compile and run.  Wipe out the index
  270. files (DEL CLASS.IX* or ERA CLASS.IX*.) and run again; note that the
  271. index files are rebuilt.  Add a new key field: Location (^QF Search
  272. for "{@" for suggested changes); re-compile/run program; note that new
  273. index file is built; note ease of adding key fields.
  274.  
  275. {Beginning of Example Program}  *** older example left in for illustration ***
  276.  
  277. PROGRAM MultiKey_Example;
  278. {$R+}
  279. CONST
  280.    MaxDataRecSize = 150;
  281.    MaxKeylen      = 30; {@ Check to add new key; will it fit? Yes.}
  282.    PageSize       = 16;
  283.    Order          = 8;
  284.    PageStackSize  = 8;
  285.    MaxHeight      = 7;
  286.  
  287.    MaxKeys        = 3; {Must appear before $I MULTIKEY.BOX}
  288.  {@MaxKeys        = 4;  Change to add new key}
  289.  
  290. {$I ACCESS.BOX    }  *** these are the old Dbox routines ***
  291. {$I GETKEY.BOX    }  *** now use USE TACCESS;            ***
  292. {$I ADDKEY.BOX    }
  293. {$I DELKEY.BOX    }
  294.  
  295. {$I MULTIKEY.BOX  } {<-- This is it!} *** Now use USE MULKEY4; ***
  296.  
  297. TYPE
  298.    NameType     = ARRAY [1..25] OF CHAR;
  299.    Class_Record = RECORD  {Record for sample application}
  300.       Status:     INTEGER;   {Always allocate the first 2 bytes; Never touch}
  301.       Id:         INTEGER;   {Primary Key}
  302.       Title:      NameType;  {Secondary Key}
  303.       Instructor: NameType;  {Tertiary Key}
  304.       Location:   NameType;  {New Key, add later}
  305.       Registered: INTEGER;
  306.    END;
  307.  
  308. VAR
  309.    Rec:            Class_Record;
  310.    Indexed_File:   File_Type;   {Defined in MULTIKEY.BOX}
  311.  
  312. PROCEDURE Display_Record (VAR R: Class_Record); BEGIN
  313.    WITH R DO BEGIN
  314.       WRITELN (' Id: ', Id, ', Registered: ', Registered, ', Title: ', Title);
  315.       WRITELN (' Instructor: ', Instructor);
  316.       WRITELN (' Location:   ', Location);
  317.       WRITELN;
  318.    END;
  319. END;
  320.  
  321. PROCEDURE Pause;
  322. VAR
  323.    Ch: Char;
  324. BEGIN
  325.    WRITELN;
  326.    WRITE ('Press any key to continue...');
  327.    READ (KBD, Ch);
  328.    WRITELN
  329. END;
  330.  
  331. BEGIN {MultiKey_Example}
  332.  
  333.    InitIndex;
  334.  
  335.    WITH Indexed_File DO BEGIN
  336.       Name := 'CLASS';
  337.       RecSize := Sizeof (Rec);
  338.       Number_Of_Keys := 3;
  339.     {@Number_Of_Keys := 4;    Change to add new key}
  340.       WITH Key [1] DO BEGIN
  341.          Offset    := KeyOffset (Rec, Rec.Id);
  342.          KeyLength := Sizeof (Rec.Id);
  343.          Extension := 'IX1';
  344.          Unique    := TRUE;  {No duplicates allowed}
  345.          Upshift   := FALSE;
  346.          KeyType   := Key_Integer;
  347.       END;
  348.       WITH Key [2] DO BEGIN
  349.          Offset    := KeyOffset (Rec, Rec.Title);
  350.          KeyLength := Sizeof (Rec.Title);
  351.          Extension := 'IX2';
  352.          Unique    := FALSE;
  353.          Upshift   := TRUE;
  354.          KeyType   := Key_Char;
  355.       END;
  356.       WITH Key [3] DO BEGIN
  357.          Offset    := KeyOffset (Rec, Rec.Instructor);
  358.          KeyLength := Sizeof (Rec.Instructor);
  359.          Extension := 'IX3';
  360.          Unique    := FALSE;
  361.          Upshift   := TRUE;
  362.          KeyType   := Key_Char;
  363.       END;
  364.       {@                    Delete this line to add new key
  365.       WITH Key [4] DO BEGIN
  366.          Offset    := KeyOffset (Rec, Rec.Location);
  367.          KeyLength := Sizeof (Rec.Location);
  368.          Extension := 'IX4';
  369.          Unique    := FALSE;
  370.          Upshift   := TRUE;
  371.          KeyType   := Key_Char;
  372.       END;
  373.                             Delete this line to add new key}
  374.    END;
  375.  
  376.    Open_File (Indexed_File);
  377.    IF Indexed_File.IOError THEN BEGIN
  378.       WRITELN ('Error rebuilding index file, duplicate key.');
  379.       WRITELN ('Continuing anyway just to see what it does.');
  380.    END;
  381.  
  382.    WITH Rec DO BEGIN
  383.  
  384.       {Dummy up some data for demonstration}
  385.       WRITELN;
  386.       WRITELN ('Inserting records into database');
  387.       WRITELN ('On first run, add for Record 3 will fail (duplicate key)');
  388.       WRITELN ('On subsequent runs, three or four adds will fail');
  389.  
  390.       Id :=         1;
  391.       Title :=      'Marking Time for Managers';
  392.       Instructor := 'White, Perry             ';
  393.       Location :=   'Daily Globe Building     ';
  394.       Registered := 10;
  395.       Add_Record (Indexed_File, Rec);
  396.       IF Indexed_File.IOError THEN WRITELN ('Record ', Id, ' not added');
  397.  
  398.       Id :=         2;
  399.       Title :=      'Evading FAA Regulations  ';
  400.       Instructor := 'Yeager, Chuck            ';
  401.       Location :=   'Nevada Desert            ';
  402.       Registered := 20;
  403.       Add_Record (Indexed_File, Rec);
  404.       IF Indexed_File.IOError THEN WRITELN ('Record ', Id, ' not added');
  405.  
  406.       Id :=         3;
  407.       Title :=      'Elementary Wall Crawling ';
  408.       Instructor := 'Parker, Peter            ';
  409.       Location :=   'Apt 3B                   ';
  410.       Registered := 30;
  411.       Add_Record (Indexed_File, Rec);
  412.       IF Indexed_File.IOError THEN WRITELN ('Record ', Id, ' not added');
  413.  
  414.       Id :=         3;
  415.       Title :=      'This record has the same ';
  416.       Instructor := 'primary key as the previo';
  417.       Location :=   'us one.  Add will fail.  ';
  418.       Registered := 35;
  419.       Add_Record (Indexed_File, Rec);
  420.       IF Indexed_File.IOError THEN WRITELN ('Record ', Id, ' not added');
  421.  
  422.       Id :=         4;
  423.       Title :=      'Stalls, Spins, Wreckage  ';
  424.       Instructor := 'Yeager, Chuck            '; {Duplicate OK}
  425.       Location :=   'All over Nevada          ';
  426.       Registered := 40;
  427.       Add_Record (Indexed_File, Rec);
  428.       IF Indexed_File.IOError THEN WRITELN ('Record ', Id, ' not added');
  429.       Pause;
  430.  
  431.       {Now read them back in various ways}
  432.  
  433.       WRITELN ('Reading forward by primary key: Id');
  434.       WRITELN ('Four records, first time thru');
  435.       WRITELN;
  436.       Id := 0;
  437.       Read_Record (Indexed_File, 1, Rec);
  438.       WHILE NOT Indexed_File.IOError DO BEGIN
  439.          Display_Record (Rec);
  440.          Next_Record (Indexed_File, 1, Rec);
  441.       END;
  442.       Pause;
  443.  
  444.       WRITELN ('Reading forward by secondary key: Title');
  445.       WRITELN;
  446.       Title := '                         ';
  447.       Read_Record (Indexed_File, 2, Rec);
  448.       WHILE NOT Indexed_File.IOError DO BEGIN
  449.          Display_Record (Rec);
  450.          Next_Record (Indexed_File, 2, Rec);
  451.       END;
  452.       Pause;
  453.  
  454.       WRITELN ('Reading backward by secondary key: Title');
  455.       WRITELN;
  456.       Title := '                         ';
  457.       Read_Record (Indexed_File, 2, Rec);
  458.       Previous_Record (Indexed_File, 2, Rec); {Get to BOF}
  459.       REPEAT
  460.          Previous_Record (Indexed_File, 2, Rec);
  461.          IF NOT Indexed_File.IOError THEN Display_Record (Rec);
  462.       UNTIL Indexed_File.IOError;
  463.       Pause;
  464.  
  465.       WRITELN ('Locate by partial tertiary key: Instructor = "YEAG "');
  466.       WRITELN ('And forward from there to EOF by key: Instructor');
  467.       WRITELN;
  468.       Instructor := 'YEAG                     '; {Note upper case search}
  469.       Read_Record (Indexed_File, 3, Rec);
  470.       WHILE NOT Indexed_File.IOError DO BEGIN
  471.          Display_Record (Rec);
  472.          Next_Record (Indexed_File, 3, Rec);
  473.       END;
  474.       Pause;
  475.  
  476.       WRITELN ('Locate, Update - Record Id = 4');
  477.       WRITELN ('Update will fail (duplicate key) second time through');
  478.       Id := 4;
  479.       Read_Record (Indexed_File, 1, Rec);
  480.       IF (Id = 4) AND NOT Indexed_File.IOError THEN BEGIN
  481.          WRITELN ('  Locate OK');
  482.          Id :=         0;                            {OK to change key}
  483.          Title :=      'Luck in Aviation         ';  {""}
  484.          Instructor := 'Yeager, Chuck            ';
  485.          Location :=   'This record updated      ';
  486.          Registered := 60;
  487.          Writeln ('  Record changed...');
  488.          Display_Record (Rec);
  489.          Update_Record (Indexed_File, Rec);
  490.          IF Indexed_File.IOError THEN WRITELN ('Record ', Id, ' not updated')
  491.          ELSE WRITELN (' Update successful');
  492.       END;
  493.       Pause;
  494.  
  495.       WRITELN ('Locate, Delete - Record Id = 3');
  496.       Id := 3;
  497.       Read_Record (Indexed_File, 1, Rec);
  498.       IF (Id = 3) AND NOT Indexed_File.IOError THEN BEGIN
  499.          WRITELN ('  Locate OK');
  500.          delete_Record (Indexed_File);
  501.          IF Indexed_File.IOError THEN WRITELN ('Record ', Id, ' not deleted')
  502.          ELSE WRITELN (' Delete Successful');
  503.  
  504.          {Note... At this point, the record just deleted remains unchanged
  505.           in Rec and may be immediately added back to the database}
  506.  
  507.       END;
  508.       Pause;
  509.  
  510.       WRITELN ('Reading forward by primary key again');
  511.       Id := 0;
  512.       Read_Record (Indexed_File, 1, Rec);
  513.       WHILE NOT Indexed_File.IOError DO BEGIN
  514.          Display_Record (Rec);
  515.          Next_Record (Indexed_File, 1, Rec);
  516.       END;
  517.       Pause;
  518.  
  519.       {@                Delete this line to read by new key
  520.       WRITELN ('Reading forward by new key');
  521.       Location := '                         ';
  522.       Read_Record (Indexed_File, 4, Rec);
  523.       WHILE NOT Indexed_File.IOError DO BEGIN
  524.          Display_Record (Rec);
  525.          Next_Record (Indexed_File, 4, Rec);
  526.       END;
  527.       Pause;
  528.                         Delete this line to read by new key}
  529.    END;
  530.  
  531.    Close_File (Indexed_File);
  532.    WRITELN ('End of Demo');
  533. END.
  534.  
  535. {End of Example Program}
  536.  
  537. *)
  538.