home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d9xx / d953 / isam.lha / ISAM / RexxISAM.doc < prev    next >
Text File  |  1993-11-28  |  28KB  |  623 lines

  1.  
  2.                            ==================
  3.                            = ARexx and ISAM =
  4.                            ==================
  5.  
  6.   Except for the addition of two record-handling functions, described later,
  7. rexxisam.library corresponds almost exactly to isam.library, as the ARexx
  8. functions actually call the functions in isam.library.  Therefore, ARexx
  9. users should still refer to the AutoDocs file as well as the main documen-
  10. tation file as your principle source of information.
  11.  
  12.   That having been said, there are a few differences in calling the various
  13. functions.
  14.  
  15.   - The most obvious difference is the presence of the new function EndISAM.
  16.     EndISAM is used by rexxisam.library as a signal that you no longer need
  17.     isam.library.  isam.library is therefore closed.  isam.library was auto-
  18.     matically opened when your first ISAM function was called.
  19.  
  20.     The reason rexxisam.library is necessary, and its functions not just
  21.     added to isam.library, is that when ARexx calls a function library,
  22.     it opens the library, calls the function, and then closes the library.
  23.  
  24.     Since closing isam.library indicates that you no longer need to use ISAM,
  25.     at that point, any ISAM files that are still open are closed automat-
  26.     ically.
  27.  
  28.     So, supposing that you call OpenISAMFile: ARexx opens isam.library and
  29.     calls OpenISAMFIle, ISAM opens your file for you, ARexx closes isam.
  30.     library, ISAM closes your file again.  Oops.
  31.  
  32.     We get around this problem by using rexx.library.  This library is, of
  33.     course, opened/closed just as isam.library was in our example above.
  34.     But opening rexx.library doesn't open isam.library, and closing rexx.
  35.     library doesn't close isam.library.
  36.     rexx.library has been designed to look for isam.library being open
  37.     whenever it is asked to call an ISAM function.  If it is open, it calls
  38.     the function.  If not, it opens the library and then calls the function.
  39.     Since the closing of isam.library is not automatic, it is necessary to
  40.     explicitly tell rexx.library to close it.  This is done by calling
  41.     EndISAM.  EndISAM takes no parameters.
  42.  
  43.   - the next most obvious difference is that ISAMWhy now has two parameters.
  44.     ISAMWhy ordinarily has one parameter (the error number) and returns
  45.     a string containing the text of the error message.  From ARexx, this
  46.     has been changed so that the ISAMWhy function now returns in the same
  47.     manner as every other function - it returns a numerical value that
  48.     is zero if the function succeeds, and non-zero otherwise.  The second
  49.     parameter is now the error message text.
  50.  
  51.   - note that in calling ISAM functions (or, indeed, ANY functions in ARexx),
  52.     there must be NO SPACE between the end of the function name, and the
  53.     opening parenthesis.
  54.  
  55.     Ex.  CloseISAMFile(  ISAMHandle )  is OK, but...
  56.          CloseISAMFile ( ISAMHandle )  will cause an ARexx error.
  57.  
  58.   - note also, that since there are often quite a few parameters in ISAM
  59.     functions, that if the function call is divided between two or more
  60.     lines, that an extra comma is needed at the end of all but the last
  61.     line (as one normally divides the line between parameters), because
  62.     ARexx will otherwise interpret the parameter-dividing comma as a
  63.     continuation comma, and will concatenate the parameter with the first
  64.     parameter on the next line.
  65.  
  66.     Ex.  error = OpenISAMFile( "SPECS:employee.specs", 1,     <---WRONG
  67.                                'R', 0, "IH" )
  68.          error = OpenISAMFile( "SPECS:employee.specs", 1, ,   <---RIGHT
  69.                                'R', 0, "IH" )
  70.  
  71.   - ARexx deals with alphanumeric text strings only - numerical data that
  72.     a C or assembler programmer would often use in keys and records (such
  73.     as UBYTE, WORD, DOUBLE, etc. ) cannot be directly handled in ARexx.
  74.  
  75.     Also, ISAM often needs to know an ADDRESS of an area of memory where
  76.     a record or returned value is to be stored, and ARexx generally doesn't
  77.     deal with addresses, except in memory "getspace"d or "allocmem"ed.
  78.     This necessitated two alterations:
  79.  
  80.       - records exist only in memory obtained by the getspace or allocmem
  81.         calls, and are not directly handled by the ARexx user.  Two func-
  82.         tions, AssembleRecord and DisAssemble record, take as parameters
  83.         the variable holding the address of the record, a string denoting
  84.         the types and lengths of the record fields, and a string containing
  85.         the names of the variables that will/do contain strings representing
  86.         the record fields.  (This is simpler than it sounds).
  87.  
  88.         key values being sent to (or returned from) ISAM must also be 
  89.         held in allocated memory, so that their address may be provided.
  90.         They are manipulated in the same manner, with the same functions.
  91.  
  92.         Memory allocated with allocmem() can have several attributes, most of
  93.         which may be combined.  These attributes are specified as the 
  94.         (optional) second parameter.  The attributes are specified as a
  95.         four-byte string.  The attributes and strings are as follows:
  96.  
  97.               PUBLIC    '0000 0001'x
  98.               CHIP      '0000 0002'x
  99.               FAST      '0000 0004'x
  100.               CLEAR     '0001 0000'x
  101.  
  102.         If the attribute CLEAR is specified (alone or in combination with
  103.         another attribute), then the memory is cleared (or turned to all
  104.         zeros).  It is often a good idea to specify CLEAR, as otherwise,
  105.         the memory could contain anything, and usually nothing useful.
  106.  
  107.         Attributes may be combined by specifying the addition of two or
  108.         more attributes (visually - ARexx won't add this type of variable)
  109.         or using the bitwise-OR function.  It may be convenient to set
  110.         variables to the attribute values first.
  111.         Note that memory cannot be both FAST and CHIP.
  112.  
  113.         Ex.
  114.  
  115.              attributes = '0001 0003'x  /* combines PUBLIC, CHIP and CLEAR */
  116.              memory = allocmem( 25, attributes )
  117.  
  118.              attributes = BITOR( FAST, CLEAR )
  119.              attributes = BITOR( attributes, PUBLIC)  /* FAST/CLEAR/PUBLIC
  120.              memory = allocmem( 25, attributes )         having been 
  121.                                                          previously set. */
  122.  
  123.              attributes = BITOR( BITOR( FAST, CLEAR ), PUBLIC )  /* '' */
  124.  
  125.              attribute = CLEAR
  126.              memory = allocmem( 25, attribute )  /* gimme 25 bytes,
  127.                                                     and clear 'em first */
  128.  
  129.  
  130.       - functions returning numeric data (such as the record number or number
  131.         of counted records) must provide as that parameter a STRING containing
  132.         the NAME of the variable in which to store the data.
  133.  
  134.         Ex.  error = OpenISAMFile( "SPECS:employee.specs", 1, 'R', 0, "IH" )
  135.  
  136.              if this function returns successfully (error = 0), the variable
  137.              IH will have been set to the ISAMHandle of the file being opened.
  138.              IH would then be provided to the various other functions that
  139.              require an ISAMHandle as a parameter, with NO QUOTES
  140.  
  141.              Ex.  error = CloseISAMFile( IH )
  142.  
  143.              because, in this case, the ISAMHandle is being provided TO ISAM,
  144.              not returned BY ISAM.
  145.  
  146.         The function ISAMWhy, as mentioned above, also uses this technique
  147.         to return the error message text.
  148.         Ex.
  149.             error = ISAMWhy( 35, "Message" )
  150.  
  151.         The variable MESSAGE will contain the text of message 35, or an
  152.         empty string (if 35 is not a valid ISAM error number).
  153.  
  154.  
  155.   - any ISAM function that requires a numeric parameter (ULONG, WORD, etc.)
  156.     need only provide a normal ARexx string containing the required value.
  157.  
  158.     Parameters of BOOL type (C/assembler programmers know the two possible
  159.     values as TRUE and FALSE) require only a normal ARexx string contain-
  160.     ing the value 1 or 0.  Probably the best way (most readable) to accom-
  161.     plish this is to make two variables TRUE and FALSE, set them to 1 and 0,
  162.     respectively, and then use the variables in your function calls:
  163.  
  164.     Ex.
  165.  
  166.         TRUE = 1; FALSE = 0
  167.  
  168.         error = OpenISAMFile( "SPECS:employee.specs", TRUE, "R", FALSE, "IH" )
  169.  
  170.  
  171.   - As detailed in the main documentation, ISAM functions return zero for
  172.     success, and a number larger than zero (an error number) for failure.
  173.     It helpful to define a variable "OK" (or some such name) that, like TRUE
  174.     and FALSE above, make the program more readable.
  175.     Set the value for success to zero.
  176.  
  177.     Ex.
  178.  
  179.         OK = 0
  180.  
  181.  
  182.   - any ISAM function that requires a parameter of type CHAR (LockType)
  183.     should provide a normal ARexx string with the desired value in the first
  184.     character position.  Anything beyond the first character will be ignored. 
  185.  
  186.   - any memory containing record/key data allocated with getspace() or alloc-
  187.     mem() should free the memory (with freespace()/freemem(), respectively)
  188.     before the program ends.  While it is true that memory allocated with
  189.     getspace() will automatically be freed at program end, it is always good
  190.     programming practice to explicitly free memory you have allocated.
  191.     Memory allocated with allocmem() MUST be explicitly freed using freemem(),
  192.     or it will be lost until the next reboot.
  193.  
  194.  
  195.   - as with any function library (like the support library rexxsupport.library)
  196.     you must add rexxisam.library to ARexx's list of libraries, before any
  197.     functions in it can be called.  You may also want to remove the library
  198.     from the list when your program has finished using ISAM (probably at the
  199.     end of the program).
  200.     (For this to work, rexxisam.library must have been placed in the LIBS:
  201.     assigned directory.)
  202.  
  203.     Ex.
  204.         if ( ~show( 'L', 'rexxisam.library' ) )
  205.           then
  206.             do
  207.               if ( ~addlib( 'rexxisam.library', 0, -30, 0 ) )
  208.                 then
  209.                   do
  210.                     say "Couldn't open rexxisam library."
  211.                     exit 10
  212.                 end
  213.             end
  214.  
  215.     ...        
  216.  
  217.         if ( show( 'L', 'rexxisam.library' ) )  then
  218.           remlib( 'rexxisam.library' )
  219.         
  220.         
  221.   - The example program books.c has been re-written in ARexx, with some
  222.     small modification.  Take a look at the code to get a better idea how
  223.     to incorporate ISAM into your programs, and appropriate whatever you
  224.     like for your own use.
  225.  
  226.  
  227.  
  228.                           ---------------------
  229.                           - THE NEW FUNCTIONS -
  230.                           ---------------------
  231.  
  232. AssembleRecord( Record, RecLen, Types, VarNames )
  233.  
  234.     - takes a string of types and a string of variable names and creates a
  235.       record of data with the indicated types at the indicated address, and
  236.       limits the record to the indicated length.
  237.  
  238.       Ex.
  239.           AssembleRecord( RecAddr, 32, "s20 u4 f8", "Name NumYears Salary" )
  240.  
  241.       takes the first 20 characters of the string contained in the variable
  242.       Name, the numerical variable NumYears expressed as a four-byte un-
  243.       signed integer (ULONG), and the numerical variable Salary expressed
  244.       as an eight-byte floating point (DOUBLE), and places them one directly
  245.       after each other at the address RecAddr.  If the various type lengths
  246.       exceeded 32 (in this case), an error would have been returned.
  247.  
  248.       Note that it does what you say, whether it is wrong or not: using this
  249.       example, suppose Name contained the string "five", which is not 20
  250.       characters long.  Wherever Name is stored, the letters f i v e will
  251.       be transferred to RecAddr, along with the next 16 bytes, which are
  252.       NOT part of Name, and could be part of some other variable, or some-
  253.       thing else entirely...  If AssembleRecord is expecting a numeric
  254.       string, and gets "fred" instead, the number zero may be stored, or
  255.       maybe something else.
  256.       Try to make sure strings contain the proper data.
  257.       Text strings can be expanded to the correct length with LEFT(),
  258.       RIGHT(), and other functions, and variables may be checked for
  259.       containing numeric data with DATATYPE( string,  <option> ), where
  260.       <option> is "numeric" (any number) or "whole" (integer number).
  261.  
  262.  
  263.       types   interpretation   C equivilent(s)       permissible lengths
  264.       -----   --------------   ------------------    -------------------
  265.         s         string       CHAR, CHAR[]/UBYTE[]  1 or larger
  266.         u    unsigned integer  UBYTE/UWORD/ULONG     1, 2, 4
  267.         i     signed integer   BYTE/WORD/LONG/int    1, 2, 4
  268.         f     floating point   FLOAT/DOUBLE          4, 8
  269.  
  270.       A particular variable name may be replaced by "*" if you do not wish
  271.       to set/change that field.  Note that if a record is assembled for the
  272.       first time, and * is specified for a field, and getspace() was used
  273.       (or allocmem(), without specifying CLEAR), then there is no way to
  274.       predict what what value that field will contain.  If allocmem() with
  275.       CLEAR is specified, then the memory will be set to all zeros, and 
  276.       fields not set will remain zero.
  277.  
  278.  
  279.       Possible errors:
  280.  
  281.           ERROR_INVALID_RECORD_LENGTH   a negative length was specified
  282.           ERROR_INVALID_KEY_TYPE        type wasn't s, u, i, or f
  283.           ERROR_INVALID_KEY_LENGTH      length was negative, or wasn't
  284.                                         valid for the type, or would result
  285.                                         in the record length being exceeded
  286.  
  287.  
  288. DisAssembleRecord( Record, RecLen, Types, VarNames [,Formats] )
  289.  
  290.     - takes the same parameters as AssembleRecord, with the addition of the
  291.       optional Formats string.  Also like AssembleRecord, a variable name
  292.       may be replaced with "*" if you don't care what the field contains and
  293.       don't wish to set a variable to its value.
  294.  
  295.       Ex.
  296.           types = "f8 f4 s3 s30"
  297.           varnames = "score avg nickname name"
  298.           error = DisAssembleRecord( RecAddr, 45, types, varnames)
  299.  
  300.       sets the variable SCORE to the floating point value stored in the
  301.       first 8 bytes, sets AVG to the floating point value stored in the
  302.       next four bytes, sets NICKNAME to a string formed by adding a null
  303.       character to the next three bytes, and sets NAME to a string formed
  304.       by adding a null character to the last 30 bytes.  (These null char-
  305.       acters are added to a copy of the record field - the record itself
  306.       is unaltered).
  307.  
  308.       As mentioned above, another parameter may be entered after the string
  309.       containing the variable names.
  310.       This parameter contains C-language "printf"-style  formatting strings.
  311.  
  312.       Numerical data is automatically formatted by a minimal printf-style
  313.       format, as appropriate to the particular type:
  314.       Ex.
  315.           type 'u' is formatted using "%u"
  316.           type 'i'                    "%d"
  317.           type 'f'                    "%f"
  318.           type 's'                    "%xs" (where x is the stated length)
  319.  
  320.       but you may substitute another format for the given one for each 
  321.       field by adding the formatting string parameter.
  322.  
  323.       Ex.
  324.           types = "f8 f4 s3 s30"
  325.           varnames = "score avg nickname name"
  326.           formats = ' %20.7f "Average is: %10.3g" %10s "%40s is his name." '
  327.           error = DisAssembleRecord( RecAddr, 45, types, varnames, formats)
  328.  
  329.       Note that, as in the example above (for the variables avg and name), if
  330.       anything but the number itself occurs in a format (spaces, or explan-
  331.       atory text) the format will have to be enclosed in DOUBLE QUOTES,
  332.       which therefore means that the  whole formatting string will need to be
  333.       enclosed in SINGLE QUOTES.  (The space after the first single quote,
  334.       and the other space before the final single quote are present just to
  335.       draw attention to the single quotes.  They are unnecessary, and do not
  336.       affect the output.)
  337.  
  338.  
  339.       (If you don't understand this formatting stuff, just ignore it and 
  340.       leave out the formatting parameter.  The fields will be automatically
  341.       formatted.  You may then change the way the resultant variable strings
  342.       look with the various ARexx internal string-altering commands/functions
  343.       after you call DisAssembleRecord.)
  344.  
  345.  
  346.  
  347.  
  348.                       --------------------------------
  349.                       - NOTES ON THE OTHER FUNCTIONS -
  350.                       --------------------------------
  351.  
  352. CountISAMRecords( ISAMHandle, KeyNo, CountMax, Count )
  353.     - remember to enclose the name of the variable to contain the record
  354.       count in quotes.
  355.  
  356. CloseISAMFile( ISAMHandle )
  357.  
  358. CreateISAMFile( SpecsFileName )
  359.  
  360. DeleteISAMFile( SpecsFileName )
  361.  
  362. DeleteISAMRecord( ISAMHandle, RecNo )
  363.  
  364. DeleteISAMRecord( ISAMHandle, KeyNo, Count )
  365.     - remember to enclose the name of the variable to contain the deleted-
  366.       record count in quotes.
  367.  
  368. GetFirstLastISAMKeyValues( ISAMHandle, KeyNo, FKeyValue, FRecNo,
  369.                                               LKeyValue, LRecNo )
  370.     - remember to enclose the name of the variables to contain the record
  371.       numbers (FRecNo/LRecNo) in quotes.
  372.  
  373.     - remember that FKeyValue/LKeyValue must be address variables (variables
  374.       that contain an address obtained from the ARexx function getspace() or
  375.       the rexxsupport.library function allocmem() ).
  376.  
  377. ISAMWhy( ErrNo, ErrText )
  378.     - remember to enclose the name of the variable to contain the error
  379.       text in quotes.
  380.  
  381. LockISAMFile( ISAMHandle, LockType )
  382.  
  383. LockISAMRecord( ISAMHandle, RecNo, LockType )
  384.  
  385. ModifyISAMRecord( ISAMHandle, RecNo, Record )
  386.     - remember that Record must be an address variable (see GetFirst...).
  387.  
  388. OpenISAMFile( SpecsFileName, LLock, LockType, SaveHead, ISAMHandle )
  389.     - remember to enclose the name of the variable to contain the ISAM
  390.       Handle in quotes.
  391.  
  392.     - remember that LLock and SaveHead need to be strings containing the
  393.       values 1 or 0 (corresponding, respectively, to TRUE or FALSE).
  394.  
  395. ReadISAMRecord( ISAMHandle, RecNO, LLock, LockType, Record )
  396.     - remember that Record must be an address variable (see GetFirst...).
  397.  
  398.     - remember that LLock needs to be a string containing the value 1 or 0
  399.       (corresponding, respectively, to TRUE or FALSE).
  400.  
  401. ReadUniqueISAMRecord( ISAMHandle, KeyNo, KeyValue, LLock,
  402.                       LockType, RecNo, Record )
  403.     - remember that KeyValue/Record must be address variables (see GetFirst...).
  404.  
  405.     - remember that LLock needs to be a string containing the value 1 or 0
  406.       (corresponding, respectively, to TRUE or FALSE).
  407.  
  408.     - remember to enclose the name of the variable to contain the record
  409.       number in quotes.
  410.  
  411. ReadNextISAMKey( ISAMHandle, KeyNo, RecNo, KeyValue )
  412.     - remember that KeyValue must be an address variable (see GetFirst...).
  413.  
  414.     - remember to enclose the name of the variable to contain the record
  415.       number in quotes.
  416.  
  417. ReadNextISAMRecord( ISAMHandle, KeyNo, LLock, Locktype, RecNo, Record )
  418.     - remember that LLock needs to be a string containing the value 1 or 0
  419.       (corresponding, respectively, to TRUE or FALSE).
  420.  
  421.     - remember to enclose the name of the variable to contain the record
  422.       number in quotes.
  423.  
  424.     - remember that Record must be an address variable (see GetFirst...).
  425.  
  426. ReIndexISAMFile( SpecsFileName, Counter )
  427.     - remember that Counter needs to be a string containing the value 1 or 0
  428.       (corresponding, respectively, to TRUE or FALSE).
  429.  
  430.     - Counter is an optional parameter.
  431.  
  432. ReportISAMStatus()
  433.  
  434. SetUpISAMIterationRange( ISAMHandle, KeyNo, IterType, KeyFrom, KeyTo )
  435.     - remember that KeyFrom/KeyTo must be address variables (see GetFirst...).
  436.  
  437.     - In the AutoDocs, it mentions that, for certain values of IterType,
  438.       KeyFrom/KeyTo "should be NULL".  This may be accomplished in three ways:
  439.  
  440.         - Parameters may be omitted.
  441.           If KeyTo should be NULL, its parameter may be omitted entirely.
  442.           If KeyTo AND KeyFrom should be NULL, BOTH parameters may be omitted.
  443.           Ex. 
  444.               (IH, KN, 1, KF ) ---> KeyTo parm is omitted.
  445.               (IH, KN, 0 )     ---> both KeyFrom/KeyTo parms omitted.
  446.           This method will not work if KeyTo is necessary, but KeyFrom is not.
  447.    
  448.         - "empty" parameters may be used (nothing between/after the commas):
  449.           Ex. 
  450.               (IH, KN, IT, KF,    ) ---> KeyTo   parm is empty.
  451.               (IH, KN, IT,   , KT ) ---> KeyFrom parm is empty.
  452.               (IH, KN, IT,   ,    ) ---> KeyFrom/KeyTo parms both empty.
  453.           Note that the spaces between/after the commas are not necessary,
  454.           and may be left out.
  455.  
  456.         - Either/both NULL parameters may be specified by using the value
  457.           zero (a string consisting of just the character "0" ).
  458.           Ex.
  459.               (IH, KN, 1, KF, 0 ) ---> KeyTo   parm is zero.
  460.               (IH, KN, 2, 0, KT ) ---> KeyFrom parm is zero.
  461.               (IH, KN, 0, 0,  0 ) ---> both KeyFrom/KeyTo parms are zero.
  462.           This is the preferred method.
  463.  
  464.           
  465. SetUpISAMIterationKey( ISAMHandle, KeyNo, KeyValue )
  466.     - remember that KeyValue must be an address variable (see GetFirst...).
  467.  
  468. SetUpISAMIterationPrefix( ISAMHandle, KeyNo, Prefix, Len )
  469.     - remember that Prefix must be an address variable (see GetFirst...).
  470.  
  471. ShutDownISAM()
  472.  
  473. StoreISAMRecord( ISAMHandle, Record, LLock, LockType, RecNo )
  474.     - remember that Record must be an address variable (see GetFirst...).
  475.  
  476.     - remember that LLock needs to be a string containing the value 1 or 0
  477.       (corresponding, respectively, to TRUE or FALSE).
  478.  
  479.     - remember to enclose the name of the variable to contain the record
  480.       number in quotes.
  481.  
  482. UnLockISAMFile( ISAMHandle )
  483.  
  484. UnLockISAMRecord( ISAMHandle, RecNo )
  485.  
  486. UnLockAllISAMRecords( ISAMHandle )
  487.  
  488.  
  489.  
  490.  
  491.        -------------------------------------------------------------
  492.        - CREATING RECORDS/KEYS, AND MAKING THE SPECIFICATIONS FILE -
  493.        -------------------------------------------------------------
  494.  
  495. The ISAM Specifications (Specs) File needs to be created to hold information
  496. about your data.  It contains a line holding the name of the data file that
  497. will be created, another line with the name of the index file to be created,
  498. a line with the record length, and one or more lines of key information.
  499.  
  500. ISAM needs to know what information in the record is important to you, 
  501. important enough to keep in sorted order in the index file.  This is the key
  502. information.  There might be only one key, or there might be many keys.
  503.  
  504. Each key may be on a different part of the record, or maybe two keys will
  505. be on the same field(s) (one being sorted smallest-to-largest (ascending),
  506. and the other largest-to-smallest (descending) ).  Maybe one key is on the
  507. combination of an ID field and the 1st 10 characters of the LastName, and
  508. another key on the full LastName.
  509.  
  510. ISAM needs to know both where to find the key in the record, and just how
  511. large the key is.   Where it is, is called the key's offset, and is counted
  512. starting from the beginning of the record.  If the key starts at the begin-
  513. ning of the record its offset is 0 (zero) 
  514.  
  515. Let's have an example record (let's say, an employee):
  516.  
  517.            types = "u2 s30 f8 s9"
  518.            names = "BranchID Name Salary SSN"
  519.  
  520. (Where BranchID is the code for the particular branch of the company, and
  521. SSN is the Social Security Number, without the dashes). 
  522.  
  523. which we will use when we want to be able to call
  524.  
  525.            AssembleRecord( RecAddr,   , types, names )
  526.  
  527.  
  528. First, let's establish the record length.  (We will assume you will be using
  529. ARexx exclusively with this record, and hence won't need to deal with pad-
  530. bytes (see ISAM.doc) )
  531.  
  532. Well, we see that we have 4 fields, whose lengths are: 2 ("u2"), 30 ("s30"),
  533. 8 ("f8"), and 9 ("s9").  Adding: 2+30+8+9 = 49.  So, now we can say:
  534.  
  535.            AssembleRecord( RecAddr, 49, types, names )
  536.  
  537. Now, let's make a table of the fields and their offsets and lengths, so that
  538. determining keys will be easier:
  539.  
  540.       FIELD      OFFSET      LENGTH
  541.       -----------------------------
  542.       BranchID     0            2
  543.       Name         2 (=0+2)    30
  544.       Salary      32 (=2+30)    8
  545.       SSN         40 (=32+8)    9
  546.                 -------------
  547.                   49 (=40+9)
  548.  
  549. Note that the offset for a field is the offset for the previous field plus
  550. the length of the previous field.  Note also, that if there were another
  551. field after the others, its offset would be the current record length.
  552. This is a good way to check your math for record length: the last offset plus
  553. the last length better equal the record length.
  554.  
  555. OK.  Now we know where and how large the fields are.  Now, let's decide
  556. what keys we want.
  557.  
  558. Well, we probably want to be able to find the employee by name, so let's
  559. make that the first key:  offset = 2.  The full name is pretty long.
  560. Surely we don't want to waste disk space indexing the full name, so let's
  561. limit it to 10.  Length = 10.  We'll assume that the last name will be
  562. first, followed by first (and middle?).  It'll be a Text ("T") type, (so
  563. it'll always be in alphabetical order, regardless of case) and we'll prob-
  564. ably want it to be ascending, so we'll get the A's first. Finally, it ought
  565. to be a repeatable key ("R"), so that if John Smith is hired and you already
  566. have a John Smith working for you, it won't reject the second one (you don't
  567. want to have to tell the higher-ups that you couldn't hire John Smith because
  568. you already HAD one, do you?)  This, the first key, will be referred to as
  569. key #0.
  570.  
  571. OK.  Next, the Social Security Number.  The government will want to track
  572. your employee's taxes, and they do everything by number...  Offset  = 40,
  573. Length = 9.  It doesn't matter if the key is of type Ascii ("A") or Text,
  574. as there will be only numbers in this field/key and numbers don't have a
  575. case.  Just for the fun of it, let's make this one Descending ("D").  Social
  576. Security Numbers are unique to each person, so we'll make this a Unique
  577. ("U") key.  Key #1.
  578.  
  579. Finally, we'd like to be able to list the employees by branch office, so a
  580. key on BranchID would seem to be in order.  However, if we just make a key
  581. on that one field, when we list all employees for one branch, the employees
  582. for that branch will be listed randomly (probably in an order similar to
  583. that in which their records were first entered into the system).  We can
  584. fix that:  extend the key to cover the BranchID AND the name field.  Once
  585. again, let's limit the amount of the Name used to 10 characters.  So:
  586. Offset = 0, Length = 12 (2 from BranchID + the first 10 of Name).  The Type
  587. will have to be Ascii ("A").  Why?  If it is type Text, then it is case-
  588. sensitive, and will be case sensitive along its whole length, not just the
  589. Name portion.  Thus if one of the two bytes in the BranchID numeric field
  590. happens to correspond to an upper- or lower-case letter, it will be stored
  591. together with those of the other case - ex: (using just one byte) if the
  592. two BranchID's are 82 and 114, they will be listed together, even though
  593. 82 and 114 are way apart numerically, because they correspond to the upper-
  594. and lower-case "R".  Using type "A" will fix that problem.  It will also
  595. give a slightly different order to the names themselves than it would 
  596. under type "T", that the earlier Name key used.  (If this were an actual
  597. assignment, you might reconsider your choice of type "T" earlier for Key #0,
  598. just for the sake of consistancy.  Or you might not.)  We'll want Ascending
  599. ("A") again, so we'll still get the names in A-Z order.  Once again, we'll
  600. want to allow keys to repeat (so more than one Jane Doe can work at the same
  601. branch) ("R").  Last (Third) Key: Key #2.
  602.  
  603. So, we may now make the Specs File (remembering that the key lines have
  604. their info in this order: offset, length, type, Ascending/Descending,
  605. Unique/Repeatable):
  606.  
  607. ----------------------------------------------------
  608. BR32:Employee.data
  609. BR32:Employee.index
  610. 49
  611. 2  10 T A R
  612. 40 9  A D U
  613. 0  12 A A R
  614. ----------------------------------------------------
  615. Where BR32 is an Assign'd logical device name.
  616.   
  617. If we called the specs file Employee.specs, and placed it in the same loca-
  618. tion as the data/index files, we would then call the CreateISAMFile command
  619. with "BR32:Employee.specs" as the command argument.  The Employee ISAM file
  620. would then be ready to use.
  621.  
  622.  
  623.