home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / r / reindx.zip / REINDEX.PAS < prev    next >
Pascal/Delphi Source File  |  1992-10-16  |  14KB  |  399 lines

  1. {*********************************************************}
  2. {*                  REINDEX.PAS 5.40                     *}
  3. {*     Copyright (c) Enz EDV Beratung GmbH 1986-90.      *}
  4. {*                 All rights reserved.                  *}
  5. {*          Modified and used under license by           *}
  6. {*                 TurboPower Software.                  *}
  7. {*********************************************************}
  8.  
  9. {$S-,R-,V-,I-,B-,F+}
  10.  
  11. {$IFNDEF Ver40}
  12.   {Allow overlays}
  13.   {$I-,O+,A-}
  14. {$ENDIF}
  15.  
  16. {Definition of the network interface and other conditional defines}
  17. {$I BTDEFINE.INC}
  18.  
  19. {BTDEFINE.INC may not change the following options}
  20. {$R-,I-}
  21.  
  22. unit Reindex;
  23.  
  24. interface
  25.  
  26. uses
  27.   Dos,
  28.   Filer;
  29.  
  30. procedure ReIndexFileBlock(FBlName : IsamFileBlockName;
  31.                            DatSLen : LongInt;
  32.                            NumberOfKeys : Integer;
  33.                            IID : IsamIndDescr;
  34.                            FuncBuildKey : Pointer);
  35.   {-This procedure reindexes the fileblock of name <FBlName> with possibly
  36.     different keys. Every non-deleted data record must be preceded by the long
  37.     int 0 in order for this to work. This is similar to the RebuildFileBlock
  38.     call, except it does not require space for the ".SAV" file.
  39.     ReindexFileBlock does not compress out deleted records, nor does it
  40.     reconstruct the header of the data file (see NOTE below).
  41.  
  42.     1) Rename the ".DAT" file to ".SAV" if no ".SAV" file exists.
  43.     2) <MakeFileBlock> with the name <FBlName>, data record length <DatSLen>,
  44.        <NumberOfKeys> of keys, and the index descriptor <IID>.
  45.     3) Close the new FileBlock, delete new ".DAT" file, and rename ".SAV"
  46.        file back to ".DAT".
  47.     4) <OpenFileBlock>
  48.     5) For every single key (1 to <NumberOfKeys>), read every data record from
  49.        the new ".DAT" file and if not a deleted record add the key with
  50.        <AddKey>.
  51.     6) Close the new fileblock.
  52.     7) Verify the data file header record for number of records and deleted
  53.        records.
  54.  
  55.     The user must write a function that builds the desired key from the data
  56.     record in order to carry out step 5. This function's address must be
  57.     passed in <FuncBuildKey>. This procedure must explicitly declared as FAR
  58.     ($F+ directive) or be exported from another unit (which automatically
  59.     makes it a FAR). An example is given below.
  60.  
  61.     The procedure is immediately aborted if a severe I/O error occurs during
  62.     the construction.
  63.  
  64.     The data record with all the keys that have been entered is deleted from
  65.     the fileblock if a duplicate key is detected during the rebuild. The
  66.     contents of the data record, along with its corresponding keys, are
  67.     written to a file with a ".MSG" extension. This may later be examined with
  68.     "Type." No file with a ".MSG" extension exists after the rebuild if there
  69.     were no duplicate keys found during the reconstruction.
  70.  
  71.     NOTE: While the header record is not rebuilt, two critical fields of the
  72.     header are verified. The number of deleted records and the number of used
  73.     records are counted while rebuilding the keys. These numbers are compared
  74.     with the values found in the header of the data file. If they do not
  75.     match, a new IsamError 8000 will be generated. If 8000 is generated,
  76.     the file will have been reindexed to the best of REINDEX's ability, but
  77.     the integrity of the data file is suspect. It is recommended that a
  78.     RebuildFileBlock be used to properly recreate the header record in the
  79.     event of IsamError 8000.
  80.  
  81.     The flag maintained within the data file header that indicates whether
  82.     the index file was left open is automatically cleared by
  83.     ReindexFileBlock. In case the number of keys has changed, the NumberOfKeys
  84.     passed explicitly to ReindexFileBlock is also written to the data file
  85.     header.
  86.  
  87.     ReindexFileBlock should not be used with FileBlocks that have space
  88.     preallocated to them through PreallocateFileBlock.
  89.  
  90.     Note that ReindexFileBlock does not support the third pathname that
  91.     is normally available for RebuildFileBlock, the SAV file drive/directory.
  92.     }
  93.  
  94. implementation
  95.  
  96.   function NumRecsInFile(IFBPtr : IsamFileBlockPtr) : LongInt;
  97.     {-Returns the number of records in a data file by the following formula:
  98.       NumRecs = (SizeOfFileInBytes div RecordLength) - 1
  99.       NOTE: Will not work correctly if PreallocateFileBlock has been used on
  100.       the FileBlock.}
  101.   var
  102.     Size : LongInt;
  103.   begin
  104.     with IFBPtr^ do begin
  105.       IsamLongSeekEOF(DatF, Size);
  106.       if not IsamOK then begin
  107.         NumRecsInFile := 0;
  108.         Exit;
  109.       end;
  110.       NumRecsInFile := (Size div DIDPtr^[0]^.LenRec)-1;
  111.     end;
  112.   end;
  113.  
  114.   procedure ReindexFileBlock(FBlName : IsamFileBlockName;
  115.                              DatSLen : LongInt;
  116.                              NumberOfKeys : Integer;
  117.                              IID : IsamIndDescr;
  118.                              FuncBuildKey : Pointer);
  119.   var
  120.     BufPtr : ^Byte;
  121.     LPtr : ^LongInt;
  122.     DLenW : Word;
  123.     L : LongInt;
  124.     NrOfRecs : LongInt;
  125.     DatSRead : LongInt;
  126.     NumberDeleted : LongInt;
  127.     DatSWritten : LongInt;
  128.     I : Integer;
  129.     J : Integer;
  130.     DontUseKey : Integer;
  131.     ReorgIFBPtr : IsamFileBlockPtr;
  132.     IKS : IsamKeyStr;
  133.     ReorgF : IsamFile;
  134.     HeaderRec : IsamSmallInfoRec;
  135.     FNameD : IsamFileBlockName;
  136.     FNameI : IsamFileBlockName;
  137.     FNameS : IsamFileBlockName;
  138.     MessageFileOpened : Boolean;
  139.     MessageFile : Text;
  140.  
  141.     function BuildKey(UserRoutine : Pointer;
  142.                       var DatS; KeyNr : Integer) : IsamKeyStr;
  143.       function CallUserRoutine(var DatS; KeyNr : Integer) : IsamKeyStr;
  144.       inline($FF/$5E/<UserRoutine); {call far dword ptr [bp+<UserRoutine]}
  145.     begin
  146.       BuildKey := CallUserRoutine(DatS, KeyNr);
  147.     end;
  148.  
  149.     procedure ReXUserRoutine(UserRoutine : Pointer; KeyNr : Integer;
  150.                              DatSNrR : LongInt; DatSNrW : LongInt;
  151.                              var DatS; Len : Word);
  152.       procedure CallUserRoutine(KeyNr : Integer;
  153.                                 DatSNrR : LongInt;  DatSNrW : LongInt;
  154.                                 var DatS; Len : Word);
  155.       inline($FF/$5E/<UserRoutine); {call far dword ptr [bp+<UserRoutine]}
  156.     begin
  157.       CallUserRoutine(KeyNr, DatSNrR, DatSNrW, DatS, Len);
  158.     end;
  159.  
  160.     procedure CreateSavFile;
  161.       {-Rename or copy the DAT file to create the SAV file}
  162.     begin
  163.       IsamAssign(ReorgF, IsamForceExtension(FNameD, DatExtension));
  164.       IsamRename(ReorgF, IsamForceExtension(FNameS, SavExtension));
  165.       if not IsamOK then
  166.         if not IsamExists(IsamForceExtension(FNameS, SavExtension)) then begin
  167.           IsamError := 10410;
  168.           Exit;
  169.         end else
  170.           IsamClearOK;
  171.     end;
  172.  
  173.     procedure UnDo(Error : Integer);
  174.     var
  175.       Dummy : Integer;
  176.     begin
  177.       IsamClose(ReorgF);
  178.       FreeMem(BufPtr, DLenW);
  179.       BTCloseFileBlock(ReorgIFBPtr);
  180.       if MessageFileOpened then
  181.         Close(MessageFile);
  182.       Dummy := IoResult;
  183.       if Error = 10413 then
  184.         {Reorg was aborted}
  185.         if IsamExists(IsamForceExtension(FNameS, SavExtension)) then
  186.           {Delete DAT and IX files, which are incomplete}
  187.           BTDeleteFileBlock(FBlName)
  188.         else begin
  189.           {Delete IX file, which is incomplete}
  190.           IsamAssign(ReorgF, IsamForceExtension(FNameI, IxExtension));
  191.           IsamDelete(ReorgF);
  192.           {Rename DAT to SAV}
  193.           IsamClearOK;
  194.           CreateSavFile;
  195.           if not IsamOK then
  196.             {A severe error occurred in CreateSavFile}
  197.             Exit;
  198.         end;
  199.       IsamOK := False;
  200.       IsamError := Error;
  201.     end;
  202.  
  203.     function UpdateMessageFile : Boolean;
  204.       {-Open and update message file, returning False if error}
  205.     var
  206.       CPtr : ^Char;
  207.       W : Word;
  208.       IoRes : Integer;
  209.     begin
  210.       UpdateMessageFile := False;
  211.       if not MessageFileOpened then begin
  212.         Assign(MessageFile, IsamForceExtension(FNameD, MsgExtension));
  213.         Rewrite(MessageFile);
  214.         IORes := IoResult;
  215.         if IORes <> 0 then begin
  216.           UnDo(IORes);
  217.           Exit;
  218.         end;
  219.         MessageFileOpened := True;
  220.       end;
  221.       WriteLn(MessageFile, 'Key ', IKS);
  222.       WriteLn(MessageFile, 'with the number ', I, ' duplicate!');
  223.       WriteLn(MessageFile, 'Data record - Dump follows');
  224.       CPtr := @BufPtr^;
  225.       for W := 1 to DLenW do begin
  226.         Write(MessageFile, CPtr^);
  227.         inc(LongInt(CPtr));
  228.       end;
  229.       WriteLn(MessageFile, ^M^J);
  230.       IoRes := IoResult;
  231.       if IORes <> 0 then begin
  232.         UnDo(IORes);
  233.         Exit;
  234.       end;
  235.       UpdateMessageFile := True;
  236.     end;
  237.  
  238.   begin
  239.     {Initialize}
  240.     IsamClearOK;
  241.     MessageFileOpened := False;
  242.  
  243.     {Separate the pathnames}
  244.     IsamExtractFileNames(FBlName, FNameD, FNameI);
  245.     {Note: unique SAV file directory not supported here}
  246.     FNameS := FNameD;
  247.  
  248.     {Validate the record length}
  249.     if DatSLen > LongInt(65535) then begin
  250.       IsamOK := False;
  251.       IsamError := 10412;
  252.       Exit;
  253.     end;
  254.  
  255.     {Create the SAV file if necessary}
  256.     CreateSavFile;
  257.     if not IsamOK then
  258.       Exit;
  259.  
  260.     {Create the new output file and close it}
  261.     BTCreateFileBlock(FNameD+';'+FNameI, DatSLen, NumberOfKeys, IID);
  262.     if not IsamOK then
  263.       Exit;
  264.  
  265.     {Allocate the input record buffer}
  266.     DLenW := ILI(DatSLen).Lo;
  267.     if MaxAvail < DLenW then begin
  268.       IsamOK := False;
  269.       IsamError := 10411;
  270.       Exit;
  271.     end;
  272.     GetMem(BufPtr, DLenW);
  273.     LPtr := @BufPtr^;
  274.  
  275.     {Open the SAV file to read the system record}
  276.     IsamAssign(ReorgF, IsamForceExtension(FNameS, SavExtension));
  277.     IsamReset(ReorgF, False, False);
  278.     if not IsamOK then begin
  279.       UnDo(IsamError);
  280.       Exit;
  281.     end;
  282.     IsamBlockRead(ReorgF, HeaderRec, SizeOf(HeaderRec));
  283.     if not IsamOK then begin
  284.       UnDo(IsamError);
  285.       Exit;
  286.     end;
  287.  
  288.     {Set the (potentially different) number of keys in the header}
  289.     HeaderRec.Gener[5] := LongInt(NumberOfKeys);
  290.     {Clear the index file open flag}
  291.     HeaderRec.ADK := False;
  292.  
  293.     {Write the header back to the SAV file and close it}
  294.     IsamLongSeek(ReorgF, 0);
  295.     IsamBlockWrite(ReorgF, HeaderRec, SizeOf(HeaderRec));
  296.     IsamClose(ReorgF);
  297.     if not IsamOK then begin
  298.       UnDo(IsamError);
  299.       Exit;
  300.     end;
  301.  
  302.     {Erase the newly created (empty) data file}
  303.     IsamAssign(ReorgF, IsamForceExtension(FNameD, DatExtension));
  304.     IsamDelete(ReorgF);
  305.     if not IsamOK then begin
  306.       UnDo(IsamError);
  307.       Exit;
  308.     end;
  309.  
  310.     {Rename the SAV file to the new DAT file}
  311.     IsamAssign(ReorgF, IsamForceExtension(FNameS, SavExtension));
  312.     IsamRename(ReorgF, IsamForceExtension(FNameD, DatExtension));
  313.     if not IsamOK then begin
  314.       UnDo(IsamError);
  315.       Exit;
  316.     end;
  317.  
  318.     {Open the fileblock, which has all the data but none of the indexes}
  319.     BTOpenFileBlock(ReorgIFBPtr, FNameD+';'+FNameI,
  320.                     False, False, False, False);
  321.     if not IsamOK then begin
  322.       UnDo(IsamError);
  323.       Exit;
  324.     end;
  325.  
  326.     {Add the keys of each index to the new fileblock}
  327.     NrOfRecs := NumRecsInFile(ReorgIFBPtr);
  328.     for I := 1 to NumberOfKeys do begin
  329.       DatSWritten := LongInt(0);
  330.       NumberDeleted := 0;
  331.       DatSRead := 0;
  332.       for L := LongInt(1) to NrOfRecs do begin
  333.         BTGetRec(ReorgIFBPtr, L, BufPtr^, False);
  334.         if not IsamOK then begin
  335.           UnDo(IsamError);
  336.           Exit;
  337.         end;
  338.         if LPtr^ <> LongInt(0) then
  339.           Inc(NumberDeleted)
  340.         else begin
  341.           Inc(DatSRead);
  342.  
  343.           {Get the key string and add it to the index}
  344.           IKS := BuildKey(FuncBuildKey, BufPtr^, I);
  345.           if IsamOK then
  346.             if AddNullKeys or (IKS <> '') then begin
  347.               BTAddKey(ReorgIFBPtr, I, L, IKS);
  348.               if IsamOK then
  349.                 Inc(DatSWritten)
  350.               else if IsamError = 10230 then begin
  351.                 {Duplicate key, report it, delete this record, and continue}
  352.                 if not UpdateMessageFile then
  353.                   Exit;
  354.                 for J := 1 to I-1 do
  355.                   BTDeleteKey(ReorgIFBPtr, J, L,
  356.                               BuildKey(FuncBuildKey, BufPtr^, J));
  357.                 BTDeleteRec(ReorgIFBPtr, L);
  358.               end else begin
  359.                 UnDo(IsamError);
  360.                 Exit;
  361.               end;
  362.             end;
  363.         end;
  364.  
  365.         {Call the user routine}
  366.         if IsamOK then
  367.           if IsamReXUserProcPtr <> nil then
  368.             ReXUserRoutine(IsamReXUserProcPtr, I, L, DatSWritten,
  369.                            BufPtr^, DLenW);
  370.  
  371.         if not IsamOK then begin
  372.           UnDo(10413);
  373.           Exit;
  374.         end;
  375.       end; {for L := 1 to NrOfRecs}
  376.     end; {for I := 1 to NumberOfKeys}
  377.  
  378.     {Free data buffer and close up}
  379.     FreeMem(BufPtr, DLenW);
  380.     if MessageFileOpened then begin
  381.       Close(MessageFile);
  382.       I := IoResult;
  383.     end;
  384.     BTCloseFileBlock(ReorgIFBPtr);
  385.  
  386.     {Check the record counts}
  387.     with HeaderRec do
  388.       if (DatSRead <> (Gener[3]-Gener[2])) or
  389.          (NumberDeleted <> Gener[2]) then begin
  390.         IsamOK := False;
  391.         IsamError := 8000;
  392.       end;
  393.   end;
  394.  
  395.   {$IFDEF InitAllUnits}
  396. begin
  397. {$ENDIF}
  398. end.
  399.