|
@@ -1,4 +1,4 @@
|
|
-unit Dbf_IdxFile;
|
|
|
|
|
|
+unit dbf_idxfile;
|
|
|
|
|
|
interface
|
|
interface
|
|
|
|
|
|
@@ -156,7 +156,6 @@ type
|
|
procedure RecalcWeight;
|
|
procedure RecalcWeight;
|
|
procedure UpdateWeight;
|
|
procedure UpdateWeight;
|
|
procedure Flush;
|
|
procedure Flush;
|
|
- procedure DisableRange;
|
|
|
|
|
|
|
|
property Key: PChar read GetKeyData;
|
|
property Key: PChar read GetKeyData;
|
|
property Entry: Pointer read FEntry;
|
|
property Entry: Pointer read FEntry;
|
|
@@ -221,7 +220,7 @@ type
|
|
FIndexHeaders: array[0..MaxIndexes-1] of Pointer;
|
|
FIndexHeaders: array[0..MaxIndexes-1] of Pointer;
|
|
FHeaderModified: array[0..MaxIndexes-1] of Boolean;
|
|
FHeaderModified: array[0..MaxIndexes-1] of Boolean;
|
|
FIndexHeader: Pointer;
|
|
FIndexHeader: Pointer;
|
|
- FIndexVersion: xBaseVersion;
|
|
|
|
|
|
+ FIndexVersion: TXBaseVersion;
|
|
FRoots: array[0..MaxIndexes-1] of TIndexPage;
|
|
FRoots: array[0..MaxIndexes-1] of TIndexPage;
|
|
FLeaves: array[0..MaxIndexes-1] of TIndexPage;
|
|
FLeaves: array[0..MaxIndexes-1] of TIndexPage;
|
|
FCurrentParser: TDbfParser;
|
|
FCurrentParser: TDbfParser;
|
|
@@ -240,11 +239,14 @@ type
|
|
FModifyMode: TIndexModifyMode;
|
|
FModifyMode: TIndexModifyMode;
|
|
FHeaderLocked: Integer; // used to remember which header page we have locked
|
|
FHeaderLocked: Integer; // used to remember which header page we have locked
|
|
FKeyBuffer: array[0..100] of Char;
|
|
FKeyBuffer: array[0..100] of Char;
|
|
|
|
+ FLowBuffer: array[0..100] of Char;
|
|
|
|
+ FHighBuffer: array[0..100] of Char;
|
|
FEntryBof: Pointer;
|
|
FEntryBof: Pointer;
|
|
FEntryEof: Pointer;
|
|
FEntryEof: Pointer;
|
|
FDbfFile: Pointer;
|
|
FDbfFile: Pointer;
|
|
FCanEdit: Boolean;
|
|
FCanEdit: Boolean;
|
|
FOpened: Boolean;
|
|
FOpened: Boolean;
|
|
|
|
+ FRangeActive: Boolean;
|
|
FUpdateMode: TIndexUpdateMode;
|
|
FUpdateMode: TIndexUpdateMode;
|
|
FUserKey: PChar; // find / insert key
|
|
FUserKey: PChar; // find / insert key
|
|
FUserRecNo: Integer; // find / insert recno
|
|
FUserRecNo: Integer; // find / insert recno
|
|
@@ -269,16 +271,26 @@ type
|
|
procedure ClearRoots;
|
|
procedure ClearRoots;
|
|
function CalcTagOffset(AIndex: Integer): Pointer;
|
|
function CalcTagOffset(AIndex: Integer): Pointer;
|
|
|
|
|
|
- function FindKey(const Insert: Boolean): Integer;
|
|
|
|
|
|
+ function FindKey(Insert: boolean): Integer;
|
|
procedure InsertKey(Buffer: PChar);
|
|
procedure InsertKey(Buffer: PChar);
|
|
procedure DeleteKey(Buffer: PChar);
|
|
procedure DeleteKey(Buffer: PChar);
|
|
procedure InsertCurrent;
|
|
procedure InsertCurrent;
|
|
procedure DeleteCurrent;
|
|
procedure DeleteCurrent;
|
|
procedure UpdateCurrent(PrevBuffer, NewBuffer: PChar);
|
|
procedure UpdateCurrent(PrevBuffer, NewBuffer: PChar);
|
|
procedure ReadIndexes;
|
|
procedure ReadIndexes;
|
|
|
|
+ procedure Resync(Relative: boolean);
|
|
procedure ResyncRoot;
|
|
procedure ResyncRoot;
|
|
procedure ResyncTree;
|
|
procedure ResyncTree;
|
|
|
|
+ procedure ResyncRange(KeepPosition: boolean);
|
|
|
|
+ procedure ResetRange;
|
|
|
|
+ procedure SetBracketLow;
|
|
|
|
+ procedure SetBracketHigh;
|
|
|
|
|
|
|
|
+ procedure WalkFirst;
|
|
|
|
+ procedure WalkLast;
|
|
|
|
+ function WalkPrev: boolean;
|
|
|
|
+ function WalkNext: boolean;
|
|
|
|
+
|
|
procedure TranslateToANSI(Src, Dest: PChar);
|
|
procedure TranslateToANSI(Src, Dest: PChar);
|
|
function CompareKeyNumericNDX(Key: PChar): Integer;
|
|
function CompareKeyNumericNDX(Key: PChar): Integer;
|
|
function CompareKeyNumericMDX(Key: PChar): Integer;
|
|
function CompareKeyNumericMDX(Key: PChar): Integer;
|
|
@@ -330,6 +342,7 @@ type
|
|
|
|
|
|
procedure CreateIndex(FieldDesc, TagName: string; Options: TIndexOptions);
|
|
procedure CreateIndex(FieldDesc, TagName: string; Options: TIndexOptions);
|
|
function ExtractKeyFromBuffer(Buffer: PChar): PChar;
|
|
function ExtractKeyFromBuffer(Buffer: PChar): PChar;
|
|
|
|
+ function SearchKey(Key: PChar; SearchType: TSearchKeyType): Boolean;
|
|
function Find(RecNo: Integer; Buffer: PChar): Integer;
|
|
function Find(RecNo: Integer; Buffer: PChar): Integer;
|
|
function IndexOf(const AIndexName: string): Integer;
|
|
function IndexOf(const AIndexName: string): Integer;
|
|
|
|
|
|
@@ -343,19 +356,15 @@ type
|
|
function Next: Boolean;
|
|
function Next: Boolean;
|
|
function Prev: Boolean;
|
|
function Prev: Boolean;
|
|
|
|
|
|
- function GetBookMark: rBookmarkData;
|
|
|
|
- function GotoBookmark(IndexBookmark: rBookmarkData): Boolean;
|
|
|
|
-
|
|
|
|
- procedure SetBracketLow;
|
|
|
|
- procedure SetBracketHigh;
|
|
|
|
|
|
+ procedure SetRange(LowRange, HighRange: PChar);
|
|
procedure CancelRange;
|
|
procedure CancelRange;
|
|
- function MatchKey: Integer;
|
|
|
|
|
|
+ function MatchKey(UserKey: PChar): Integer;
|
|
function CompareKey(Key: PChar): Integer;
|
|
function CompareKey(Key: PChar): Integer;
|
|
function CompareKeys(Key1, Key2: PChar): Integer;
|
|
function CompareKeys(Key1, Key2: PChar): Integer;
|
|
function PrepareKey(Buffer: PChar; ResultType: TExpressionType): PChar;
|
|
function PrepareKey(Buffer: PChar; ResultType: TExpressionType): PChar;
|
|
|
|
|
|
property KeyLen: Integer read GetKeyLen;
|
|
property KeyLen: Integer read GetKeyLen;
|
|
- property IndexVersion: xBaseVersion read FIndexVersion;
|
|
|
|
|
|
+ property IndexVersion: TXBaseVersion read FIndexVersion;
|
|
property EntryHeaderSize: Integer read FEntryHeaderSize;
|
|
property EntryHeaderSize: Integer read FEntryHeaderSize;
|
|
property KeyType: Char read GetKeyType;
|
|
property KeyType: Char read GetKeyType;
|
|
|
|
|
|
@@ -828,7 +837,7 @@ function TIndexPage.FindNearest(ARecNo: Integer): Integer;
|
|
// Result = 0 -> key,recno found, FEntryNo = found key entryno
|
|
// Result = 0 -> key,recno found, FEntryNo = found key entryno
|
|
// Result > 0 -> key,recno larger than current entry
|
|
// Result > 0 -> key,recno larger than current entry
|
|
var
|
|
var
|
|
- recNo, low, high: Integer;
|
|
|
|
|
|
+ low, high, current: Integer;
|
|
begin
|
|
begin
|
|
// implement binary search, keys are sorted
|
|
// implement binary search, keys are sorted
|
|
low := FLowIndex;
|
|
low := FLowIndex;
|
|
@@ -845,8 +854,8 @@ begin
|
|
// vf: high + 1 - low
|
|
// vf: high + 1 - low
|
|
while low < high do
|
|
while low < high do
|
|
begin
|
|
begin
|
|
- FEntryNo := (low + high) div 2;
|
|
|
|
- FEntry := GetEntry(FEntryNo);
|
|
|
|
|
|
+ current := (low + high) div 2;
|
|
|
|
+ FEntry := GetEntry(current);
|
|
// calc diff
|
|
// calc diff
|
|
Result := MatchKey;
|
|
Result := MatchKey;
|
|
// test if we need to go lower or higher
|
|
// test if we need to go lower or higher
|
|
@@ -854,68 +863,56 @@ begin
|
|
// result = 0 implies key equal to tested entry
|
|
// result = 0 implies key equal to tested entry
|
|
// result > 0 implies key greater than tested entry
|
|
// result > 0 implies key greater than tested entry
|
|
if (Result < 0) or ((ARecNo<>-3) and (Result=0)) then
|
|
if (Result < 0) or ((ARecNo<>-3) and (Result=0)) then
|
|
- high := FEntryNo
|
|
|
|
|
|
+ high := current
|
|
else
|
|
else
|
|
- low := FEntryNo+1;
|
|
|
|
|
|
+ low := current+1;
|
|
end;
|
|
end;
|
|
// high will contain first greater-or-equal key
|
|
// high will contain first greater-or-equal key
|
|
// ARecNo <> -3 -> Entry(high).Key will contain first key that matches -> go to high
|
|
// ARecNo <> -3 -> Entry(high).Key will contain first key that matches -> go to high
|
|
// ARecNo = -3 -> Entry(high).Key will contain first key that is greater -> go to high
|
|
// ARecNo = -3 -> Entry(high).Key will contain first key that is greater -> go to high
|
|
- recNo := high;
|
|
|
|
- if FEntryNo <> recNo then
|
|
|
|
- begin
|
|
|
|
- FEntryNo := recNo;
|
|
|
|
- FEntry := GetEntry(recNo);
|
|
|
|
- end;
|
|
|
|
|
|
+ FEntryNo := -1;
|
|
|
|
+ EntryNo := high;
|
|
// calc end result: can't inspect high if lowerpage <> nil
|
|
// calc end result: can't inspect high if lowerpage <> nil
|
|
// if this is a leaf, we need to find specific recno
|
|
// if this is a leaf, we need to find specific recno
|
|
if (LowerPage = nil) then
|
|
if (LowerPage = nil) then
|
|
begin
|
|
begin
|
|
- // FLowerPage = nil -> can inspect high
|
|
|
|
- Result := MatchKey;
|
|
|
|
- // test if we need to find a specific recno
|
|
|
|
- // result < 0 -> current key greater -> nothing found -> don't search
|
|
|
|
- if (ARecNo > 0) then
|
|
|
|
|
|
+ if high > FHighIndex then
|
|
begin
|
|
begin
|
|
- // BLS to RecNo
|
|
|
|
- high := FHighIndex + 1;
|
|
|
|
- low := FEntryNo;
|
|
|
|
- // inv: FLowIndex <= FEntryNo <= high <= FHighIndex + 1 /\
|
|
|
|
- // (Ai: FLowIndex <= i < FEntryNo: Entry(i).RecNo <> ARecNo)
|
|
|
|
- while FEntryNo <> high do
|
|
|
|
|
|
+ Result := 1;
|
|
|
|
+ end else begin
|
|
|
|
+ Result := MatchKey;
|
|
|
|
+ // test if we need to find a specific recno
|
|
|
|
+ // result < 0 -> current key greater -> nothing found -> don't search
|
|
|
|
+ if (ARecNo > 0) then
|
|
begin
|
|
begin
|
|
- // FEntryNo < high, get new entry
|
|
|
|
- if low <> FEntryNo then
|
|
|
|
|
|
+ // BLS to RecNo
|
|
|
|
+ high := FHighIndex + 1;
|
|
|
|
+ low := FEntryNo;
|
|
|
|
+ // inv: FLowIndex <= FEntryNo <= high <= FHighIndex + 1 /\
|
|
|
|
+ // (Ai: FLowIndex <= i < FEntryNo: Entry(i).RecNo <> ARecNo)
|
|
|
|
+ while FEntryNo <> high do
|
|
begin
|
|
begin
|
|
- FEntry := GetEntry(FEntryNo);
|
|
|
|
- // check if entry key still ok
|
|
|
|
- Result := MatchKey;
|
|
|
|
- end;
|
|
|
|
- // get recno of current item
|
|
|
|
- recNo := GetRecNo;
|
|
|
|
- // test if out of range or found
|
|
|
|
- if (Result <> 0) or (recNo = ARecNo) then
|
|
|
|
- high := FEntryNo
|
|
|
|
- else begin
|
|
|
|
- // default to EOF
|
|
|
|
- inc(FEntryNo);
|
|
|
|
- Result := 1;
|
|
|
|
|
|
+ // FEntryNo < high, get new entry
|
|
|
|
+ if low <> FEntryNo then
|
|
|
|
+ begin
|
|
|
|
+ FEntry := GetEntry(FEntryNo);
|
|
|
|
+ // check if entry key still ok
|
|
|
|
+ Result := MatchKey;
|
|
|
|
+ end;
|
|
|
|
+ // test if out of range or found recno
|
|
|
|
+ if (Result <> 0) or (GetRecNo = ARecNo) then
|
|
|
|
+ high := FEntryNo
|
|
|
|
+ else begin
|
|
|
|
+ // default to EOF
|
|
|
|
+ inc(FEntryNo);
|
|
|
|
+ Result := 1;
|
|
|
|
+ end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
- // if not found, get EOF entry
|
|
|
|
- if (Result <> 0) then
|
|
|
|
- begin
|
|
|
|
- // Entry(FEntryNo) <> Entry
|
|
|
|
- // bypass SetEntryNo check
|
|
|
|
- FEntryNo := -1;
|
|
|
|
- EntryNo := high;
|
|
|
|
- end;
|
|
|
|
end;
|
|
end;
|
|
end else begin
|
|
end else begin
|
|
// FLowerPage <> nil -> high contains entry, can not have empty range
|
|
// FLowerPage <> nil -> high contains entry, can not have empty range
|
|
Result := 0;
|
|
Result := 0;
|
|
- // sync lower page
|
|
|
|
- SyncLowerPage;
|
|
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
@@ -1222,15 +1219,6 @@ begin
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
-procedure TIndexPage.DisableRange;
|
|
|
|
-begin
|
|
|
|
- // update low / high index range
|
|
|
|
- FLowIndex := 0;
|
|
|
|
- FHighIndex := GetNumEntries;
|
|
|
|
- if FLowerPage = nil then
|
|
|
|
- dec(FHighIndex);
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
function TMdxPage.GetIsInnerNode: Boolean;
|
|
function TMdxPage.GetIsInnerNode: Boolean;
|
|
begin
|
|
begin
|
|
Result := PMdxPage(FPageBuffer).NumEntries < PIndexHdr(FIndexFile.IndexHeader).NumKeys;
|
|
Result := PMdxPage(FPageBuffer).NumEntries < PIndexHdr(FIndexFile.IndexHeader).NumKeys;
|
|
@@ -1704,6 +1692,7 @@ begin
|
|
|
|
|
|
// clear variables
|
|
// clear variables
|
|
FOpened := false;
|
|
FOpened := false;
|
|
|
|
+ FRangeActive := false;
|
|
FUpdateMode := umCurrent;
|
|
FUpdateMode := umCurrent;
|
|
FModifyMode := mmNormal;
|
|
FModifyMode := mmNormal;
|
|
FTempMode := TDbfFile(ADbfFile).TempMode;
|
|
FTempMode := TDbfFile(ADbfFile).TempMode;
|
|
@@ -2746,6 +2735,9 @@ begin
|
|
end else begin
|
|
end else begin
|
|
InsertKey(Buffer);
|
|
InsertKey(Buffer);
|
|
end;
|
|
end;
|
|
|
|
+
|
|
|
|
+ // check range, disabled by insert
|
|
|
|
+ ResyncRange(true);
|
|
end;
|
|
end;
|
|
|
|
|
|
function TIndexFile.CheckKeyViolation(Buffer: PChar): Boolean;
|
|
function TIndexFile.CheckKeyViolation(Buffer: PChar): Boolean;
|
|
@@ -2818,14 +2810,20 @@ begin
|
|
begin
|
|
begin
|
|
IntSrc := PInteger(Result)^;
|
|
IntSrc := PInteger(Result)^;
|
|
// handle zero differently: no decimals
|
|
// handle zero differently: no decimals
|
|
- NumDecimals := GetStrFromInt(IntSrc, @FloatRec.Digits[0]);
|
|
|
|
|
|
+ if IntSrc <> 0 then
|
|
|
|
+ NumDecimals := GetStrFromInt(IntSrc, @FloatRec.Digits[0])
|
|
|
|
+ else
|
|
|
|
+ NumDecimals := 0;
|
|
FloatRec.Negative := IntSrc < 0;
|
|
FloatRec.Negative := IntSrc < 0;
|
|
end;
|
|
end;
|
|
{$ifdef SUPPORT_INT64}
|
|
{$ifdef SUPPORT_INT64}
|
|
etLargeInt:
|
|
etLargeInt:
|
|
begin
|
|
begin
|
|
Int64Src := PLargeInt(Result)^;
|
|
Int64Src := PLargeInt(Result)^;
|
|
- NumDecimals := GetStrFromInt64(Int64Src, @FloatRec.Digits[0]);
|
|
|
|
|
|
+ if Int64Src <> 0 then
|
|
|
|
+ NumDecimals := GetStrFromInt64(Int64Src, @FloatRec.Digits[0])
|
|
|
|
+ else
|
|
|
|
+ NumDecimals := 0;
|
|
FloatRec.Negative := Int64Src < 0;
|
|
FloatRec.Negative := Int64Src < 0;
|
|
end;
|
|
end;
|
|
{$endif}
|
|
{$endif}
|
|
@@ -2833,7 +2831,10 @@ begin
|
|
begin
|
|
begin
|
|
ExtValue := PDouble(Result)^;
|
|
ExtValue := PDouble(Result)^;
|
|
FloatToDecimal(FloatRec, ExtValue, {$ifndef FPC_VERSION}fvExtended,{$endif} 9999, 15);
|
|
FloatToDecimal(FloatRec, ExtValue, {$ifndef FPC_VERSION}fvExtended,{$endif} 9999, 15);
|
|
- NumDecimals := StrLen(@FloatRec.Digits[0]);
|
|
|
|
|
|
+ if ExtValue <> 0.0 then
|
|
|
|
+ NumDecimals := StrLen(@FloatRec.Digits[0])
|
|
|
|
+ else
|
|
|
|
+ NumDecimals := 0;
|
|
// maximum number of decimals possible to encode in BCD is 16
|
|
// maximum number of decimals possible to encode in BCD is 16
|
|
if NumDecimals > 16 then
|
|
if NumDecimals > 16 then
|
|
NumDecimals := 16;
|
|
NumDecimals := 16;
|
|
@@ -2905,7 +2906,6 @@ procedure TIndexFile.InsertCurrent;
|
|
// insert in current index
|
|
// insert in current index
|
|
// assumes: FUserKey is an OEM key
|
|
// assumes: FUserKey is an OEM key
|
|
var
|
|
var
|
|
- TempPage: TIndexPage;
|
|
|
|
SearchKey: array[0..100] of Char;
|
|
SearchKey: array[0..100] of Char;
|
|
OemKey: PChar;
|
|
OemKey: PChar;
|
|
begin
|
|
begin
|
|
@@ -2920,6 +2920,8 @@ begin
|
|
FUserKey := @SearchKey[0];
|
|
FUserKey := @SearchKey[0];
|
|
TranslateToANSI(OemKey, FUserKey);
|
|
TranslateToANSI(OemKey, FUserKey);
|
|
end;
|
|
end;
|
|
|
|
+ // temporarily remove range to find correct location of key
|
|
|
|
+ ResetRange;
|
|
// find this record as closely as possible
|
|
// find this record as closely as possible
|
|
// if result = 0 then key already exists
|
|
// if result = 0 then key already exists
|
|
// if unique index, then don't insert key if already present
|
|
// if unique index, then don't insert key if already present
|
|
@@ -2940,13 +2942,6 @@ begin
|
|
InsertError;
|
|
InsertError;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
-
|
|
|
|
- // check range, disabled by insert
|
|
|
|
- TempPage := FRoot;
|
|
|
|
- repeat
|
|
|
|
- TempPage.UpdateBounds(TempPage.LowerPage <> nil);
|
|
|
|
- TempPage := TempPage.LowerPage;
|
|
|
|
- until TempPage = nil;
|
|
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
@@ -2980,6 +2975,8 @@ begin
|
|
end else begin
|
|
end else begin
|
|
DeleteKey(Buffer);
|
|
DeleteKey(Buffer);
|
|
end;
|
|
end;
|
|
|
|
+ // range may be changed
|
|
|
|
+ ResyncRange(true);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TIndexFile.DeleteKey(Buffer: PChar);
|
|
procedure TIndexFile.DeleteKey(Buffer: PChar);
|
|
@@ -3003,6 +3000,8 @@ begin
|
|
// modify = mmDeleteRecall /\ unique = distinct -> key needs to be deleted from index
|
|
// modify = mmDeleteRecall /\ unique = distinct -> key needs to be deleted from index
|
|
if (FModifyMode <> mmDeleteRecall) or (FUniqueMode = iuDistinct) then
|
|
if (FModifyMode <> mmDeleteRecall) or (FUniqueMode = iuDistinct) then
|
|
begin
|
|
begin
|
|
|
|
+ // prevent "confined" view of index while deleting
|
|
|
|
+ ResetRange;
|
|
// search correct entry to delete
|
|
// search correct entry to delete
|
|
if FLeaf.PhysicalRecNo <> FUserRecNo then
|
|
if FLeaf.PhysicalRecNo <> FUserRecNo then
|
|
begin
|
|
begin
|
|
@@ -3063,6 +3062,8 @@ begin
|
|
// now set userkey to key to insert
|
|
// now set userkey to key to insert
|
|
FUserKey := @TempBuffer[0];
|
|
FUserKey := @TempBuffer[0];
|
|
InsertCurrent;
|
|
InsertCurrent;
|
|
|
|
+ // check range, disabled by delete/insert
|
|
|
|
+ ResyncRange(true);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
@@ -3130,6 +3131,41 @@ begin
|
|
FRoot.PageNo := PIndexHdr(FIndexHeader).RootPage;
|
|
FRoot.PageNo := PIndexHdr(FIndexHeader).RootPage;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+function TIndexFile.SearchKey(Key: PChar; SearchType: TSearchKeyType): Boolean;
|
|
|
|
+var
|
|
|
|
+ findres, currRecNo: Integer;
|
|
|
|
+begin
|
|
|
|
+ // save current position
|
|
|
|
+ currRecNo := SequentialRecNo;
|
|
|
|
+ // search, these are always from the root: no need for first
|
|
|
|
+ findres := Find(-2, Key);
|
|
|
|
+ // test result
|
|
|
|
+ case SearchType of
|
|
|
|
+ stEqual:
|
|
|
|
+ Result := findres = 0;
|
|
|
|
+ stGreaterEqual:
|
|
|
|
+ Result := findres <= 0;
|
|
|
|
+ stGreater:
|
|
|
|
+ begin
|
|
|
|
+ if findres = 0 then
|
|
|
|
+ begin
|
|
|
|
+ // find next record that is greater
|
|
|
|
+ // NOTE: MatchKey assumes key to search for is already specified
|
|
|
|
+ // in FUserKey, it is because we have called Find
|
|
|
|
+ repeat
|
|
|
|
+ Result := WalkNext;
|
|
|
|
+ until not Result or (MatchKey(Key) <> 0);
|
|
|
|
+ end else
|
|
|
|
+ Result := findres < 0;
|
|
|
|
+ end;
|
|
|
|
+ else
|
|
|
|
+ Result := false;
|
|
|
|
+ end;
|
|
|
|
+ // search failed -> restore previous position
|
|
|
|
+ if not Result then
|
|
|
|
+ SequentialRecNo := currRecNo;
|
|
|
|
+end;
|
|
|
|
+
|
|
function TIndexFile.Find(RecNo: Integer; Buffer: PChar): Integer;
|
|
function TIndexFile.Find(RecNo: Integer; Buffer: PChar): Integer;
|
|
begin
|
|
begin
|
|
// execute find
|
|
// execute find
|
|
@@ -3138,7 +3174,7 @@ begin
|
|
Result := FindKey(false);
|
|
Result := FindKey(false);
|
|
end;
|
|
end;
|
|
|
|
|
|
-function TIndexFile.FindKey(const Insert: Boolean): Integer;
|
|
|
|
|
|
+function TIndexFile.FindKey(Insert: boolean): Integer;
|
|
//
|
|
//
|
|
// if you set Insert = true, you need to re-enable range after insert!!
|
|
// if you set Insert = true, you need to re-enable range after insert!!
|
|
//
|
|
//
|
|
@@ -3162,16 +3198,6 @@ begin
|
|
end else begin
|
|
end else begin
|
|
searchRecNo := -2;
|
|
searchRecNo := -2;
|
|
end;
|
|
end;
|
|
- // disable range to prepare for insert
|
|
|
|
- if Insert then
|
|
|
|
- begin
|
|
|
|
- // start from root
|
|
|
|
- TempPage := FRoot;
|
|
|
|
- repeat
|
|
|
|
- TempPage.DisableRange;
|
|
|
|
- TempPage := TempPage.LowerPage;
|
|
|
|
- until TempPage = nil;
|
|
|
|
- end;
|
|
|
|
// start from root
|
|
// start from root
|
|
TempPage := FRoot;
|
|
TempPage := FRoot;
|
|
repeat
|
|
repeat
|
|
@@ -3236,7 +3262,7 @@ begin
|
|
until done = 0;
|
|
until done = 0;
|
|
end;
|
|
end;
|
|
|
|
|
|
-function TIndexFile.MatchKey: Integer;
|
|
|
|
|
|
+function TIndexFile.MatchKey(UserKey: PChar): Integer;
|
|
begin
|
|
begin
|
|
// BOF and EOF always false
|
|
// BOF and EOF always false
|
|
if FLeaf.Entry = FEntryBof then
|
|
if FLeaf.Entry = FEntryBof then
|
|
@@ -3244,8 +3270,18 @@ begin
|
|
else
|
|
else
|
|
if FLeaf.Entry = FEntryEof then
|
|
if FLeaf.Entry = FEntryEof then
|
|
Result := -1
|
|
Result := -1
|
|
- else
|
|
|
|
|
|
+ else begin
|
|
|
|
+ FUserKey := UserKey;
|
|
Result := FLeaf.MatchKey;
|
|
Result := FLeaf.MatchKey;
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TIndexFile.SetRange(LowRange, HighRange: PChar);
|
|
|
|
+begin
|
|
|
|
+ Move(LowRange^, FLowBuffer[0], KeyLen);
|
|
|
|
+ Move(HighRange^, FHighBuffer[0], KeyLen);
|
|
|
|
+ FRangeActive := true;
|
|
|
|
+ ResyncRange(true);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TIndexFile.RecordDeleted(RecNo: Integer; Buffer: PChar);
|
|
procedure TIndexFile.RecordDeleted(RecNo: Integer; Buffer: PChar);
|
|
@@ -3264,20 +3300,6 @@ begin
|
|
FModifyMode := mmNormal;
|
|
FModifyMode := mmNormal;
|
|
end;
|
|
end;
|
|
|
|
|
|
-function TIndexFile.GotoBookmark(IndexBookmark: rBookmarkData): Boolean;
|
|
|
|
-begin
|
|
|
|
- if (IndexBookmark{.RecNo} = 0) then begin
|
|
|
|
- First;
|
|
|
|
- end else if (IndexBookmark{.RecNo} = MAXINT) then begin
|
|
|
|
- Last;
|
|
|
|
- end else begin
|
|
|
|
- if (FLeaf.GetRecNo <> IndexBookmark{.RecNo}) then
|
|
|
|
- PhysicalRecNo := IndexBookmark{.RecNo};
|
|
|
|
- end;
|
|
|
|
-
|
|
|
|
- Result := true;
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
procedure TIndexFile.SetLocaleID(const NewID: LCID);
|
|
procedure TIndexFile.SetLocaleID(const NewID: LCID);
|
|
{$ifdef WIN32}
|
|
{$ifdef WIN32}
|
|
var
|
|
var
|
|
@@ -3302,16 +3324,20 @@ end;
|
|
|
|
|
|
procedure TIndexFile.SetPhysicalRecNo(RecNo: Integer);
|
|
procedure TIndexFile.SetPhysicalRecNo(RecNo: Integer);
|
|
begin
|
|
begin
|
|
- // read buffer of this RecNo
|
|
|
|
- TDbfFile(FDbfFile).ReadRecord(RecNo, TDbfFile(FDbfFile).PrevBuffer);
|
|
|
|
- // extract key
|
|
|
|
- FUserKey := ExtractKeyFromBuffer(TDbfFile(FDbfFile).PrevBuffer);
|
|
|
|
- // translate to a search key
|
|
|
|
- if KeyType = 'C' then
|
|
|
|
- TranslateToANSI(FUserKey, FUserKey);
|
|
|
|
- // find this key
|
|
|
|
- FUserRecNo := RecNo;
|
|
|
|
- FindKey(false);
|
|
|
|
|
|
+ // check record actually exists
|
|
|
|
+ if TDbfFile(FDbfFile).IsRecordPresent(RecNo) then
|
|
|
|
+ begin
|
|
|
|
+ // read buffer of this RecNo
|
|
|
|
+ TDbfFile(FDbfFile).ReadRecord(RecNo, TDbfFile(FDbfFile).PrevBuffer);
|
|
|
|
+ // extract key
|
|
|
|
+ FUserKey := ExtractKeyFromBuffer(TDbfFile(FDbfFile).PrevBuffer);
|
|
|
|
+ // translate to a search key
|
|
|
|
+ if KeyType = 'C' then
|
|
|
|
+ TranslateToANSI(FUserKey, FUserKey);
|
|
|
|
+ // find this key
|
|
|
|
+ FUserRecNo := RecNo;
|
|
|
|
+ FindKey(false);
|
|
|
|
+ end;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TIndexFile.SetUpdateMode(NewMode: TIndexUpdateMode);
|
|
procedure TIndexFile.SetUpdateMode(NewMode: TIndexUpdateMode);
|
|
@@ -3323,28 +3349,16 @@ begin
|
|
FUpdateMode := NewMode;
|
|
FUpdateMode := NewMode;
|
|
end;
|
|
end;
|
|
|
|
|
|
-function TIndexFile.GetBookMark: rBookmarkData;
|
|
|
|
|
|
+procedure TIndexFile.WalkFirst;
|
|
begin
|
|
begin
|
|
- // get physical recno
|
|
|
|
- Result := FLeaf.GetRecNo;
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
-procedure TIndexFile.First;
|
|
|
|
-begin
|
|
|
|
- // resync tree
|
|
|
|
- if NeedLocks then
|
|
|
|
- ResyncRoot;
|
|
|
|
// search first node
|
|
// search first node
|
|
FRoot.RecurFirst;
|
|
FRoot.RecurFirst;
|
|
// out of index - BOF
|
|
// out of index - BOF
|
|
FLeaf.EntryNo := FLeaf.EntryNo - 1;
|
|
FLeaf.EntryNo := FLeaf.EntryNo - 1;
|
|
end;
|
|
end;
|
|
|
|
|
|
-procedure TIndexFile.Last;
|
|
|
|
|
|
+procedure TIndexFile.WalkLast;
|
|
begin
|
|
begin
|
|
- // resync tree
|
|
|
|
- if NeedLocks then
|
|
|
|
- ResyncRoot;
|
|
|
|
// search last node
|
|
// search last node
|
|
FRoot.RecurLast;
|
|
FRoot.RecurLast;
|
|
// out of index - EOF
|
|
// out of index - EOF
|
|
@@ -3352,40 +3366,125 @@ begin
|
|
FLeaf.EntryNo := FLeaf.EntryNo + 2;
|
|
FLeaf.EntryNo := FLeaf.EntryNo + 2;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+procedure TIndexFile.First;
|
|
|
|
+begin
|
|
|
|
+ // resync tree
|
|
|
|
+ Resync(false);
|
|
|
|
+ WalkFirst;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TIndexFile.Last;
|
|
|
|
+begin
|
|
|
|
+ // resync tree
|
|
|
|
+ Resync(false);
|
|
|
|
+ WalkLast;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TIndexFile.ResyncRange(KeepPosition: boolean);
|
|
|
|
+var
|
|
|
|
+ Result: Boolean;
|
|
|
|
+ currRecNo: integer;
|
|
|
|
+begin
|
|
|
|
+ if not FRangeActive then
|
|
|
|
+ exit;
|
|
|
|
+
|
|
|
|
+ // disable current range if any
|
|
|
|
+ if KeepPosition then
|
|
|
|
+ currRecNo := SequentialRecNo;
|
|
|
|
+ ResetRange;
|
|
|
|
+ // search lower bound
|
|
|
|
+ Result := SearchKey(FLowBuffer, stGreaterEqual);
|
|
|
|
+ if not Result then
|
|
|
|
+ begin
|
|
|
|
+ // not found? -> make empty range
|
|
|
|
+ WalkLast;
|
|
|
|
+ end;
|
|
|
|
+ // set lower bound
|
|
|
|
+ SetBracketLow;
|
|
|
|
+ // search upper bound
|
|
|
|
+ Result := SearchKey(FHighBuffer, stGreater);
|
|
|
|
+ // if result true, then need to get previous item <=>
|
|
|
|
+ // last of equal/lower than key
|
|
|
|
+ if Result then
|
|
|
|
+ begin
|
|
|
|
+ Result := WalkPrev;
|
|
|
|
+ if not Result then
|
|
|
|
+ begin
|
|
|
|
+ // cannot go prev -> empty range
|
|
|
|
+ WalkFirst;
|
|
|
|
+ end;
|
|
|
|
+ end else begin
|
|
|
|
+ // not found -> EOF found, go EOF, then to last record
|
|
|
|
+ WalkLast;
|
|
|
|
+ WalkPrev;
|
|
|
|
+ end;
|
|
|
|
+ // set upper bound
|
|
|
|
+ SetBracketHigh;
|
|
|
|
+ if KeepPosition then
|
|
|
|
+ SequentialRecNo := currRecNo;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TIndexFile.Resync(Relative: boolean);
|
|
|
|
+begin
|
|
|
|
+ if NeedLocks then
|
|
|
|
+ begin
|
|
|
|
+ if not Relative then
|
|
|
|
+ begin
|
|
|
|
+ ResyncRoot;
|
|
|
|
+ ResyncRange(false);
|
|
|
|
+ end else begin
|
|
|
|
+ // resyncing tree implies resyncing range
|
|
|
|
+ ResyncTree;
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
procedure TIndexFile.ResyncTree;
|
|
procedure TIndexFile.ResyncTree;
|
|
|
|
+var
|
|
|
|
+ action, recno: integer;
|
|
begin
|
|
begin
|
|
// if at BOF or EOF, then we need to resync by first or last
|
|
// if at BOF or EOF, then we need to resync by first or last
|
|
|
|
+ // remember where the cursor was
|
|
if FLeaf.Entry = FEntryBof then
|
|
if FLeaf.Entry = FEntryBof then
|
|
begin
|
|
begin
|
|
- First;
|
|
|
|
|
|
+ action := 0;
|
|
end else if FLeaf.Entry = FEntryEof then begin
|
|
end else if FLeaf.Entry = FEntryEof then begin
|
|
- Last;
|
|
|
|
|
|
+ action := 1;
|
|
end else begin
|
|
end else begin
|
|
// read current key into buffer
|
|
// read current key into buffer
|
|
Move(FLeaf.Key^, FKeyBuffer, PIndexHdr(FIndexHeader).KeyLen);
|
|
Move(FLeaf.Key^, FKeyBuffer, PIndexHdr(FIndexHeader).KeyLen);
|
|
- // search current in-mem key on disk
|
|
|
|
- FUserKey := FKeyBuffer;
|
|
|
|
- FUserRecNo := FLeaf.PhysicalRecNo;
|
|
|
|
// translate to searchable key
|
|
// translate to searchable key
|
|
if KeyType = 'C' then
|
|
if KeyType = 'C' then
|
|
- TranslateToANSI(FUserKey, FUserKey);
|
|
|
|
- if (FindKey(false) <> 0) then
|
|
|
|
|
|
+ TranslateToANSI(FKeyBuffer, FKeyBuffer);
|
|
|
|
+ recno := FLeaf.PhysicalRecNo;
|
|
|
|
+ action := 2;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ // we now know cursor position, resync possible range
|
|
|
|
+ ResyncRange(false);
|
|
|
|
+
|
|
|
|
+ // go to cursor position
|
|
|
|
+ case action of
|
|
|
|
+ 0: WalkFirst;
|
|
|
|
+ 1: WalkLast;
|
|
|
|
+ 2:
|
|
begin
|
|
begin
|
|
- // houston, we've got a problem!
|
|
|
|
- // our `current' record has gone. we need to find it
|
|
|
|
- // find it by using physical recno
|
|
|
|
- PhysicalRecNo := FUserRecNo;
|
|
|
|
|
|
+ // search current in-mem key on disk
|
|
|
|
+ if (Find(recno, FKeyBuffer) <> 0) then
|
|
|
|
+ begin
|
|
|
|
+ // houston, we've got a problem!
|
|
|
|
+ // our `current' record has gone. we need to find it
|
|
|
|
+ // find it by using physical recno
|
|
|
|
+ PhysicalRecNo := recno;
|
|
|
|
+ end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
-function TIndexFile.Prev: Boolean;
|
|
|
|
|
|
+function TIndexFile.WalkPrev: boolean;
|
|
var
|
|
var
|
|
curRecNo: Integer;
|
|
curRecNo: Integer;
|
|
begin
|
|
begin
|
|
- // resync in-mem tree with tree on disk
|
|
|
|
- if NeedLocks then
|
|
|
|
- ResyncTree;
|
|
|
|
// save current recno, find different next!
|
|
// save current recno, find different next!
|
|
curRecNo := FLeaf.PhysicalRecNo;
|
|
curRecNo := FLeaf.PhysicalRecNo;
|
|
repeat
|
|
repeat
|
|
@@ -3394,13 +3493,10 @@ begin
|
|
until not Result or (curRecNo <> FLeaf.PhysicalRecNo);
|
|
until not Result or (curRecNo <> FLeaf.PhysicalRecNo);
|
|
end;
|
|
end;
|
|
|
|
|
|
-function TIndexFile.Next: Boolean;
|
|
|
|
|
|
+function TIndexFile.WalkNext: boolean;
|
|
var
|
|
var
|
|
curRecNo: Integer;
|
|
curRecNo: Integer;
|
|
begin
|
|
begin
|
|
- // resync in-mem tree with tree on disk
|
|
|
|
- if NeedLocks then
|
|
|
|
- ResyncTree;
|
|
|
|
// save current recno, find different prev!
|
|
// save current recno, find different prev!
|
|
curRecNo := FLeaf.PhysicalRecNo;
|
|
curRecNo := FLeaf.PhysicalRecNo;
|
|
repeat
|
|
repeat
|
|
@@ -3409,6 +3505,20 @@ begin
|
|
until not Result or (curRecNo <> FLeaf.PhysicalRecNo);
|
|
until not Result or (curRecNo <> FLeaf.PhysicalRecNo);
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+function TIndexFile.Prev: Boolean;
|
|
|
|
+begin
|
|
|
|
+ // resync in-mem tree with tree on disk
|
|
|
|
+ Resync(true);
|
|
|
|
+ Result := WalkPrev;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TIndexFile.Next: Boolean;
|
|
|
|
+begin
|
|
|
|
+ // resync in-mem tree with tree on disk
|
|
|
|
+ Resync(true);
|
|
|
|
+ Result := WalkNext;
|
|
|
|
+end;
|
|
|
|
+
|
|
function TIndexFile.GetKeyLen: Integer;
|
|
function TIndexFile.GetKeyLen: Integer;
|
|
begin
|
|
begin
|
|
Result := PIndexHdr(FIndexHeader).KeyLen;
|
|
Result := PIndexHdr(FIndexHeader).KeyLen;
|
|
@@ -3514,6 +3624,12 @@ begin
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TIndexFile.CancelRange;
|
|
procedure TIndexFile.CancelRange;
|
|
|
|
+begin
|
|
|
|
+ FRangeActive := false;
|
|
|
|
+ ResetRange;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TIndexFile.ResetRange;
|
|
var
|
|
var
|
|
TempPage: TIndexPage;
|
|
TempPage: TIndexPage;
|
|
begin
|
|
begin
|