📄 dbf_dbffile.pas
字号:
lColumnCount := (PDbfHdr(Header)^.FullHdrSize - lHeaderSize) div lFieldSize;
lFieldOffset := 1;
lAutoInc := 0;
I := 1;
lCurrentNullPosition := 0;
lCanHoldNull := false;
try
// there has to be minimum of one field
repeat
// version field info?
if FDbfVersion >= xBaseVII then
begin
ReadRecord(I, @lFieldDescVII);
lFieldName := AnsiUpperCase(PChar(@lFieldDescVII.FieldName[0]));
lSize := lFieldDescVII.FieldSize;
lPrec := lFieldDescVII.FieldPrecision;
lNativeFieldType := lFieldDescVII.FieldType;
lAutoInc := SwapIntLE(lFieldDescVII.NextAutoInc);
if lNativeFieldType = '+' then
FAutoIncPresent := true;
end else begin
ReadRecord(I, @lFieldDescIII);
lFieldName := AnsiUpperCase(PChar(@lFieldDescIII.FieldName[0]));
lSize := lFieldDescIII.FieldSize;
lPrec := lFieldDescIII.FieldPrecision;
lNativeFieldType := lFieldDescIII.FieldType;
lCanHoldNull := (FDbfVersion = xFoxPro) and
((lFieldDescIII.FoxProFlags and $2) <> 0) and
(lFieldName <> '_NULLFLAGS');
end;
// apply field transformation tricks
if (lNativeFieldType = 'C')
{$ifndef USE_LONG_CHAR_FIELDS}
and (FDbfVersion = xFoxPro)
{$endif}
then
begin
lSize := lSize + lPrec shl 8;
lPrec := 0;
end;
// add field
TempFieldDef := FFieldDefs.AddFieldDef;
with TempFieldDef do
begin
FieldName := lFieldName;
Offset := lFieldOffset;
Size := lSize;
Precision := lPrec;
AutoInc := lAutoInc;
NativeFieldType := lNativeFieldType;
if lCanHoldNull then
begin
NullPosition := lCurrentNullPosition;
inc(lCurrentNullPosition);
end else
NullPosition := -1;
end;
// check valid field:
// 1) non-empty field name
// 2) known field type
// {3) no changes have to be made to precision or size}
if (Length(lFieldName) = 0) or (TempFieldDef.FieldType = ftUnknown) then
raise EDbfError.Create(STRING_INVALID_DBF_FILE);
// determine if lock field present, if present, then store additional info
if lFieldName = '_DBASELOCK' then
begin
FLockField := TempFieldDef;
FLockUserLen := lSize - 8;
if FLockUserLen > DbfGlobals.UserNameLen then
FLockUserLen := DbfGlobals.UserNameLen;
end else
if UpperCase(lFieldName) = '_NULLFLAGS' then
FNullField := TempFieldDef;
// goto next field
Inc(lFieldOffset, lSize);
Inc(I);
// continue until header termination character found
// or end of header reached
until (I > lColumnCount) or (ReadChar = $0D);
// test if not too many fields
if FFieldDefs.Count >= 4096 then
raise EDbfError.CreateFmt(STRING_INVALID_FIELD_COUNT, [FFieldDefs.Count]);
// do not check FieldOffset = PDbfHdr(Header).RecordSize because additional
// data could be present in record
// get current position
lPropHdrOffset := Stream.Position;
// dBase 7 -> read field properties, test if enough space, maybe no header
if (FDbfVersion = xBaseVII) and (lPropHdrOffset + Sizeof(lFieldPropsHdr) <
PDbfHdr(Header)^.FullHdrSize) then
begin
// read in field properties header
ReadBlock(@lFieldPropsHdr, SizeOf(lFieldPropsHdr), lPropHdrOffset);
// read in standard properties
lFieldOffset := lPropHdrOffset + lFieldPropsHdr.StartStdProps;
for I := 0 to lFieldPropsHdr.NumStdProps - 1 do
begin
// read property data
ReadBlock(@lStdProp, SizeOf(lStdProp), lFieldOffset+I*SizeOf(lStdProp));
// is this a constraint?
if lStdProp.FieldOffset = 0 then
begin
// this is a constraint...not implemented
end else if lStdProp.FieldOffset <= FFieldDefs.Count then begin
// get fielddef for this property
TempFieldDef := FFieldDefs.Items[lStdProp.FieldOffset-1];
// allocate space to store data
TempFieldDef.AllocBuffers;
// dataPtr = nil -> no data to retrieve
dataPtr := nil;
// store data
case lStdProp.PropType of
FieldPropType_Required: TempFieldDef.Required := true;
FieldPropType_Default:
begin
dataPtr := TempFieldDef.DefaultBuf;
TempFieldDef.HasDefault := true;
end;
FieldPropType_Min:
begin
dataPtr := TempFieldDef.MinBuf;
TempFieldDef.HasMin := true;
end;
FieldPropType_Max:
begin
dataPtr := TempFieldDef.MaxBuf;
TempFieldDef.HasMax := true;
end;
end;
// get data for this property
if dataPtr <> nil then
ReadBlock(dataPtr, lStdProp.DataSize, lPropHdrOffset + lStdProp.DataOffset);
end;
end;
// read custom properties...not implemented
// read RI properties...not implemented
end;
finally
HeaderSize := PDbfHdr(Header)^.FullHdrSize;
RecordSize := PDbfHdr(Header)^.RecordSize;
end;
end;
function TDbfFile.GetLanguageId: Integer;
begin
Result := PDbfHdr(Header)^.Language;
end;
function TDbfFile.GetLanguageStr: String;
begin
if FDbfVersion >= xBaseVII then
Result := PAfterHdrVII(PChar(Header) + SizeOf(rDbfHdr))^.LanguageDriverName;
end;
{
I fill the holes with the last records.
now we can do an 'in-place' pack
}
procedure TDbfFile.FastPackTable;
var
iDel,iNormal: Integer;
pDel,pNormal: PChar;
function FindFirstDel: Boolean;
begin
while iDel<=iNormal do
begin
ReadRecord(iDel, pDel);
if (PChar(pDel)^ <> ' ') then
begin
Result := true;
exit;
end;
Inc(iDel);
end;
Result := false;
end;
function FindLastNormal: Boolean;
begin
while iNormal>=iDel do
begin
ReadRecord(iNormal, pNormal);
if (PChar(pNormal)^= ' ') then
begin
Result := true;
exit;
end;
dec(iNormal);
end;
Result := false;
end;
begin
if RecordSize < 1 then Exit;
GetMem(pNormal, RecordSize);
GetMem(pDel, RecordSize);
try
iDel := 1;
iNormal := RecordCount;
while FindFirstDel do
begin
// iDel is definitely deleted
if FindLastNormal then
begin
// but is not anymore
WriteRecord(iDel, pNormal);
PChar(pNormal)^ := '*';
WriteRecord(iNormal, pNormal);
end else begin
// Cannot found a record after iDel so iDel must be deleted
dec(iDel);
break;
end;
end;
// FindFirstDel failed means than iDel is full
RecordCount := iDel;
RegenerateIndexes;
// Pack Memofields
finally
FreeMem(pNormal);
FreeMem(pDel);
end;
end;
procedure TDbfFile.Rename(DestFileName: string; NewIndexFileNames: TStrings; DeleteFiles: boolean);
var
lIndexFileNames: TStrings;
lIndexFile: TIndexFile;
NewBaseName: string;
I: integer;
begin
// get memory for index file list
lIndexFileNames := TStringList.Create;
try
// save index filenames
for I := 0 to FIndexFiles.Count - 1 do
begin
lIndexFile := TIndexFile(IndexFiles[I]);
lIndexFileNames.Add(lIndexFile.FileName);
// prepare changing the dbf file name, needs changes in index files
lIndexFile.PrepareRename(NewIndexFileNames[I]);
end;
// close file
Close;
if DeleteFiles then
begin
SysUtils.DeleteFile(DestFileName);
SysUtils.DeleteFile(ChangeFileExt(DestFileName, GetMemoExt));
end else begin
I := 0;
FindNextName(DestFileName, NewBaseName, I);
SysUtils.RenameFile(DestFileName, NewBaseName);
SysUtils.RenameFile(ChangeFileExt(DestFileName, GetMemoExt),
ChangeFileExt(NewBaseName, GetMemoExt));
end;
// delete old index files
for I := 0 to NewIndexFileNames.Count - 1 do
SysUtils.DeleteFile(NewIndexFileNames.Strings[I]);
// rename the new dbf files
SysUtils.RenameFile(FileName, DestFileName);
SysUtils.RenameFile(ChangeFileExt(FileName, GetMemoExt),
ChangeFileExt(DestFileName, GetMemoExt));
// rename new index files
for I := 0 to NewIndexFileNames.Count - 1 do
SysUtils.RenameFile(lIndexFileNames.Strings[I], NewIndexFileNames.Strings[I]);
finally
lIndexFileNames.Free;
end;
end;
type
TRestructFieldInfo = record
SourceOffset: Integer;
DestOffset: Integer;
Size: Integer;
end;
{ assume nobody has more than 8192 fields, otherwise possibly range check error }
PRestructFieldInfo = ^TRestructFieldInfoArray;
TRestructFieldInfoArray = array[0..8191] of TRestructFieldInfo;
procedure TDbfFile.RestructureTable(DbfFieldDefs: TDbfFieldDefs; Pack: Boolean);
var
DestDbfFile: TDbfFile;
TempIndexDef: TDbfIndexDef;
TempIndexFile: TIndexFile;
DestFieldDefs: TDbfFieldDefs;
TempDstDef, TempSrcDef: TDbfFieldDef;
OldIndexFiles: TStrings;
IndexName, NewBaseName: string;
I, lRecNo, lFieldNo, lFieldSize, lBlobPageNo, lWRecNo, srcOffset, dstOffset: Integer;
pBuff, pDestBuff: PChar;
RestructFieldInfo: PRestructFieldInfo;
BlobStream: TMemoryStream;
begin
// nothing to do?
if (RecordSize < 1) or ((DbfFieldDefs = nil) and not Pack) then
exit;
// if no exclusive access, terrible things can happen!
CheckExclusiveAccess;
// make up some temporary filenames
lRecNo := 0;
FindNextName(FileName, NewBaseName, lRecNo);
// select final field definition list
if DbfFieldDefs = nil then
begin
DestFieldDefs := FFieldDefs;
end else begin
DestFieldDefs := DbfFieldDefs;
// copy autoinc values
for I := 0 to DbfFieldDefs.Count - 1 do
begin
lFieldNo := DbfFieldDefs.Items[I].CopyFrom;
if (lFieldNo >= 0) and (lFieldNo < FFieldDefs.Count) then
DbfFieldDefs.Items[I].AutoInc := FFieldDefs.Items[lFieldNo].AutoInc;
end;
end;
// create temporary dbf
DestDbfFile := TDbfFile.Create;
DestDbfFile.FileName := NewBaseName;
DestDbfFile.AutoCreate := true;
DestDbfFile.Mode := pfExclusiveCreate;
DestDbfFile.OnIndexMissing := FOnIndexMissing;
DestDbfFile.OnLocaleError := FOnLocaleError;
DestDbfFile.DbfVersion := FDbfVersion;
DestDbfFile.FileLangId := FileLangId;
DestDbfFile.Open;
// create dbf header
if FMemoFile <> nil then
DestDbfFile.FinishCreate(DestFieldDefs, FMemoFile.RecordSize)
else
DestDbfFile.FinishCreate(DestFieldDefs, 512);
// adjust size and offsets of fields
GetMem(RestructFieldInfo, sizeof(TRestructFieldInfo)*DestFieldDefs.Count);
for lFieldNo := 0 to DestFieldDefs.Count - 1 do
begin
TempDstDef := DestFieldDefs.Items[lFieldNo];
if TempDstDef.CopyFrom >= 0 then
begin
TempSrcDef := FFieldDefs.Items[TempDstDef.CopyFrom];
if TempDstDef.NativeFieldType in ['F', 'N'] then
begin
// get minimum field length
lFieldSize := Min(TempSrcDef.Precision, TempDstDef.Precision) +
Min(TempSrcDef.Size - TempSrcDef.Precision,
TempDstDef.Size - TempDstDef.Precision);
// if one has dec separator, but other not, we lose one digit
if (TempDstDef.Precision > 0) xor
((TempSrcDef.NativeFieldType in ['F', 'N']) and (TempSrcDef.Precision > 0)) then
Dec(lFieldSize);
// should not happen, but check nevertheless (maybe corrupt data)
if lFieldSize < 0 then
lFieldSize := 0;
srcOffset := TempSrcDef.Size - TempSrcDef.Precision -
(TempDstDef.Size - TempDstDef.Precision);
if srcOffset < 0 then
begin
dstOffset := -srcOffset;
srcOffset := 0;
end else begin
dstOffset := 0;
end;
end else begin
lFieldSize := Min(TempSrcDef.Size, TempDstDef.Size);
srcOffset := 0;
dstOffset := 0;
end;
with RestructFieldInfo[lFieldNo] do
begin
Size := lFieldSize;
SourceOffset := TempSrcDef.Offset + srcOffset;
DestOffset := TempDstDef.Offset + dstOffset;
end;
end;
end;
// add indexes
TempIndexDef := TDbfIndexDef.Create(nil);
for I := 0 to FIndexNames.Count - 1 do
begin
// get length of extension -> determines MDX or NDX
IndexName := FIndexNames.Strings[I];
TempIndexFile := TIndexFile(FIndexNames.Objects[I]);
TempIndexFile.GetIndexInfo(IndexName, TempIndexDef);
if Length(ExtractFileExt(IndexName)) > 0 then
begin
// NDX index, get unique file name
lRecNo := 0;
FindNextName(IndexName, IndexName, lRecNo);
end;
// add this index
DestDbfFile.OpenIndex(IndexName, TempIndexDef.SortField, true, TempIndexDef.Options);
end;
TempIndexDef.Free;
// get memory for record buffers
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -