Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

another pack alternative

Status
Not open for further replies.

Chris Miller

Programmer
Oct 28, 2020
4,756
DE
Her's the continuation of

Using low level file operations on the dbf file instead of SCATTER/GATHER.

Pros:
1. Even faster, as it doesn't touch cdx or fpt, any usual updates of indexes and memo file by scatter are avoided.
2. The readonly nature of autoinc fields is overcome
3. No index violations occur.

Cons:
Needs exclusive dbf access just like the usual PACK
1. Needs a final REINDX so the index tags become valid again - I plan to replace this by low level cdx file operations as mainly only the recno of shifted records has to change in the current index trees and nodes about now truncated non-existing records have to be removed completely.
2. Does not pack the fpt file. I could simply claim that's off my goal for this PACK alternative, but in the snes of tha pack alternative working on the existing files instead of creating new files, this could also be addressed.

Code:
Lparameters tcDBF

Local lcDeleted, lcAlias
lcDeleted = Set("Deleted")
lcAlias = Alias()

If Pcount()=0
   tcDBF = Dbf()
   USE
Endif

Local lnFile, lnLastByte
lnFile = Fopen(tcDBF,12)
If lnFile<0
   * ? "can't pack DBF"
Else
   Local lcSuffix, lnFirstDeleted, lnLastUndeleted
   lcSuffix = Sys(2015)

   Local lcAliasIsDeleted, lcAliasUnDeleted, lcRecord
   lcAliasIsDeleted = 'Deleted'+lcSuffix
   lcAliasUnDeleted = 'UnDeleted'+lcSuffix

   Set Deleted Off

   * Use the passed in DBF file twice for two record pointers on deleted and undeleted records
   Create Cursor recordmoves (nSource I, nDest I)

   Fclose(lnFile)
   Use (tcDBF) In Select(lcAliasIsDeleted) Exclusive Alias (lcAliasIsDeleted)
   Use (tcDBF) In Select(lcAliasUnDeleted) Exclusive Again Alias (lcAliasUnDeleted)
   tcDBF = Dbf(lcAliasUnDeleted)

   Erase (Getenv("TEMP")+"\IsDeleted.idx")
   Erase (Getenv("TEMP")+"\UnDeleted.idx")

   Select (lcAliasIsDeleted)
   Index On Recno() To (Getenv("TEMP")+"\IsDeleted.idx") For Deleted() Additive
   Locate
   lnFirstDeleted = Recno()

   Select (lcAliasUnDeleted)
   Index On RECCOUNT()-Recno() To (Getenv("TEMP")+"\UnDeleted.idx") For Not Deleted() Additive
   Locate
   lnLastUndeleted = Recno()

   Local lnHeader, lnRecsize
   lnHeader = Header()
   lnRecsize = Recsize()

   * While the first deleted record is before the last undeleted records, move that undeleted record to fill the gap:
   Do While lnFirstDeleted<lnLastUndeleted And ;
         Not Deleted(lcAliasUnDeleted) And Deleted(lcAliasIsDeleted) And ;
         Not Eof(lcAliasUnDeleted) And Not Eof(lcAliasIsDeleted)

      Insert Into recordmoves (nSource, nDest) Values (lnLastUndeleted-1, lnFirstDeleted-1)

      Skip 1 In (lcAliasIsDeleted)
      lnFirstDeleted = Recno(lcAliasIsDeleted)
      Skip 1 In (lcAliasUnDeleted)
      lnLastUndeleted = max(Recno(lcAliasUnDeleted),recordmoves.nDest+1)
   Enddo
   Set Order To 0
   Set Index To
   Use In Select(lcAliasIsDeleted)
   Use In Select(lcAliasUnDeleted)
   Set Deleted &lcDeleted
   Select 0
   Erase (Getenv("TEMP")+"\IsDeleted.idx")
   Erase (Getenv("TEMP")+"\UnDeleted.idx")
   lnLastByte = lnHeader+lnLastUndeleted*lnRecsize+1
   ? lnLastUndeleted, lnLastByte
   
   * process dbf with low level file operations
   lnFile = Fopen(tcDBF,2)
   SELECT recordmoves   
   Scan
      * copy undeleted row with scatter/gather
      ? Fseek(lnFile, lnHeader+nSource*lnRecsize)
      Store Fread(lnFile,lnRecsize) To lcRecord
      ? Fseek(lnFile, lnHeader+nDest*lnRecsize)
      ? Fwrite(lnFile,lcRecord,lnRecsize)
   Endscan

   * change reccount in DBF header offset 4:
   Fseek(lnFile, 4)
   Fwrite(lnFile, Chr(Bitand(lnLastUndeleted,0xff)),1)
   lnLastUndeleted = Bitrshift(lnLastUndeleted,8)
   Fwrite(lnFile, Chr(Bitand(lnLastUndeleted,0xff)),1)
   lnLastUndeleted = Bitrshift(lnLastUndeleted,8)
   Fwrite(lnFile, Chr(Bitand(lnLastUndeleted,0xff)),1)
   lnLastUndeleted = Bitrshift(lnLastUndeleted,8)
   Fwrite(lnFile, Chr(Bitand(lnLastUndeleted,0xff)),1)
   * change byte after last undeleted record to EOF (1A)
   Fseek(lnFile, lnLastByte)
   Fwrite(lnFile, Chr(0x1A),1)
   * truncate file
   Fchsize(lnFile, lnLastByte)

   SELECT 0
   Fclose(lnFile)
   Use (tcDBF) Exclusive Again Alias (lcAliasUnDeleted)
   REINDEX
   USE
   IF PCOUNT()=0
      Use (tcDBF) Shared Again Alias (lcAlias)
   ENDIF
Endif

If Not lcAlias=="" And Used(lcAlias)
   Select (lcAlias)
Endif

Chriss
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top