You've already forked lazarus-ccr
TParadoxDataset: Add filtering support.
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@6908 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -4,7 +4,7 @@
|
|||||||
<PathDelim Value="\"/>
|
<PathDelim Value="\"/>
|
||||||
<Name Value="lazparadoxpkg"/>
|
<Name Value="lazparadoxpkg"/>
|
||||||
<Type Value="RunAndDesignTime"/>
|
<Type Value="RunAndDesignTime"/>
|
||||||
<Author Value="Christian Ulrich"/>
|
<Author Value="Christian Ulrich, Werner Pamler"/>
|
||||||
<CompilerOptions>
|
<CompilerOptions>
|
||||||
<Version Value="11"/>
|
<Version Value="11"/>
|
||||||
<PathDelim Value="\"/>
|
<PathDelim Value="\"/>
|
||||||
@ -22,8 +22,8 @@
|
|||||||
</Debugging>
|
</Debugging>
|
||||||
</Linking>
|
</Linking>
|
||||||
</CompilerOptions>
|
</CompilerOptions>
|
||||||
<Description Value="Dataset for Paradox database files
|
<Description Value="Dataset for Paradox database files; support of blobs, bookmarks and filtering
|
||||||
Read-only
|
Read-only, no indexes
|
||||||
Tested on Win and Linux gtk2/qt (32bit/64bit)"/>
|
Tested on Win and Linux gtk2/qt (32bit/64bit)"/>
|
||||||
<License Value="LGPL"/>
|
<License Value="LGPL"/>
|
||||||
<Version Minor="2"/>
|
<Version Minor="2"/>
|
||||||
|
@ -10,28 +10,28 @@ unit paradoxds;
|
|||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
Classes, SysUtils, db, lconvencoding;
|
Classes, SysUtils, db, lconvencoding, bufdataset_parser;
|
||||||
|
|
||||||
|
|
||||||
const
|
const
|
||||||
{ Paradox codes for field types }
|
{ Paradox codes for field types }
|
||||||
pxfAlpha = $01;
|
pxfAlpha = $01;
|
||||||
pxfDate = $02;
|
pxfDate = $02;
|
||||||
pxfShort = $03;
|
pxfShort = $03;
|
||||||
pxfLong = $04;
|
pxfLong = $04;
|
||||||
pxfCurrency = $05;
|
pxfCurrency = $05;
|
||||||
pxfNumber = $06;
|
pxfNumber = $06;
|
||||||
pxfLogical = $09;
|
pxfLogical = $09;
|
||||||
pxfMemoBLOb = $0C;
|
pxfMemoBLOb = $0C;
|
||||||
pxfBLOb = $0D;
|
pxfBLOb = $0D;
|
||||||
pxfFmtMemoBLOb = $0E;
|
pxfFmtMemoBLOb = $0E;
|
||||||
pxfOLE = $0F;
|
pxfOLE = $0F;
|
||||||
pxfGraphic = $10;
|
pxfGraphic = $10;
|
||||||
pxfTime = $14;
|
pxfTime = $14;
|
||||||
pxfTimestamp = $15;
|
pxfTimestamp = $15;
|
||||||
pxfAutoInc = $16;
|
pxfAutoInc = $16;
|
||||||
pxfBCD = $17;
|
pxfBCD = $17;
|
||||||
pxfBytes = $18;
|
pxfBytes = $18;
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
@ -166,11 +166,15 @@ type
|
|||||||
FInputEncoding: String;
|
FInputEncoding: String;
|
||||||
FTargetEncoding: String;
|
FTargetEncoding: String;
|
||||||
FPxFields: Array of TPxField;
|
FPxFields: Array of TPxField;
|
||||||
|
FFilterBuffer : TRecordBuffer;
|
||||||
|
FParser: TBufDatasetParser;
|
||||||
function GetEncrypted: Boolean;
|
function GetEncrypted: Boolean;
|
||||||
function GetInputEncoding: String; inline;
|
function GetInputEncoding: String; inline;
|
||||||
function GetTargetEncoding: String; inline;
|
function GetTargetEncoding: String; inline;
|
||||||
function GetVersion: String;
|
function GetVersion: String;
|
||||||
function IsStoredTargetEncoding: Boolean;
|
function IsStoredTargetEncoding: Boolean;
|
||||||
|
function PxFilterRecord(Buffer: TRecordBuffer): Boolean;
|
||||||
|
function PxGetActiveBuffer(var Buffer: TRecordBuffer): Boolean;
|
||||||
procedure ReadBlock;
|
procedure ReadBlock;
|
||||||
procedure ReadNextBlockHeader;
|
procedure ReadNextBlockHeader;
|
||||||
procedure ReadPrevBlockHeader;
|
procedure ReadPrevBlockHeader;
|
||||||
@ -198,8 +202,11 @@ type
|
|||||||
procedure InternalPost; override;
|
procedure InternalPost; override;
|
||||||
procedure InternalSetToRecord(Buffer: PChar); override;
|
procedure InternalSetToRecord(Buffer: PChar); override;
|
||||||
function IsCursorOpen: Boolean; override;
|
function IsCursorOpen: Boolean; override;
|
||||||
|
procedure ParseFilter(const AFilter: string);
|
||||||
procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
|
procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
|
||||||
procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
|
procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
|
||||||
|
procedure SetFiltered(Value: Boolean); override;
|
||||||
|
procedure SetFilterText(const Value: String); override;
|
||||||
procedure SetRecNo(Value: Integer); override;
|
procedure SetRecNo(Value: Integer); override;
|
||||||
public
|
public
|
||||||
constructor Create(AOwner: TComponent); override;
|
constructor Create(AOwner: TComponent); override;
|
||||||
@ -217,6 +224,7 @@ type
|
|||||||
property Active;
|
property Active;
|
||||||
property AutoCalcFields;
|
property AutoCalcFields;
|
||||||
property FieldDefs;
|
property FieldDefs;
|
||||||
|
property Filter;
|
||||||
property Filtered;
|
property Filtered;
|
||||||
property BeforeOpen;
|
property BeforeOpen;
|
||||||
property AfterOpen;
|
property AfterOpen;
|
||||||
@ -268,7 +276,7 @@ end;
|
|||||||
|
|
||||||
function TParadoxDataset.BookmarkValid(ABookmark: TBookmark): Boolean;
|
function TParadoxDataset.BookmarkValid(ABookmark: TBookmark): Boolean;
|
||||||
begin
|
begin
|
||||||
Result := Assigned(ABookmark) and (Length(ABookMark) <> 0);
|
Result := Assigned(ABookmark) and (Length(ABookmark) <> 0);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TParadoxDataset.CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Longint;
|
function TParadoxDataset.CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Longint;
|
||||||
@ -399,13 +407,15 @@ var
|
|||||||
int: LongInt absolute s;
|
int: LongInt absolute s;
|
||||||
d: Double absolute s;
|
d: Double absolute s;
|
||||||
str: String;
|
str: String;
|
||||||
|
buf: TRecordBuffer = nil;
|
||||||
begin
|
begin
|
||||||
Result := False;
|
Result := False;
|
||||||
if (RecordCount = 0) then
|
if (RecordCount = 0) then
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
|
PXGetActiveBuffer(Buf);
|
||||||
|
p := buf + FPxFields[Field.FieldNo - 1].Offset;
|
||||||
F := FPxFields[Field.FieldNo - 1].Info;
|
F := FPxFields[Field.FieldNo - 1].Info;
|
||||||
p := ActiveBuffer + FPxFields[Field.FieldNo - 1].Offset;
|
|
||||||
size := F^.fSize;
|
size := F^.fSize;
|
||||||
|
|
||||||
// These numeric fields are stored as big endian --> swap bytes
|
// These numeric fields are stored as big endian --> swap bytes
|
||||||
@ -484,59 +494,63 @@ end;
|
|||||||
function TParadoxDataset.GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
|
function TParadoxDataset.GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
|
||||||
var
|
var
|
||||||
L: Longword;
|
L: Longword;
|
||||||
|
accepted: Boolean;
|
||||||
begin
|
begin
|
||||||
Result := grOK;
|
Result := grOK;
|
||||||
case GetMode of
|
accepted := false;
|
||||||
gmNext:
|
|
||||||
begin
|
repeat
|
||||||
inc(FaRecord);
|
case GetMode of
|
||||||
if (FaBlockIdx = FHeader^.lastBlock) and
|
gmNext:
|
||||||
(FaRecord > FaBlockStart + FaBlock^.addDataSize div FHeader^.recordSize + 1)
|
|
||||||
then
|
|
||||||
Result := grEOF
|
|
||||||
else
|
|
||||||
begin
|
begin
|
||||||
|
inc(FaRecord);
|
||||||
|
if (FaBlockIdx = FHeader^.lastBlock) and
|
||||||
|
(FaRecord > FaBlockStart + FaBlock^.addDataSize div FHeader^.recordSize + 1)
|
||||||
|
then
|
||||||
|
Result := grEOF
|
||||||
|
else
|
||||||
if FaRecord > FaBlockStart+1+(FaBlock^.addDataSize div FHeader^.recordSize) then
|
if FaRecord > FaBlockStart+1+(FaBlock^.addDataSize div FHeader^.recordSize) then
|
||||||
ReadNextBlockHeader;
|
ReadNextBlockHeader;
|
||||||
end;
|
end;
|
||||||
end;
|
gmPrior:
|
||||||
gmPrior:
|
|
||||||
begin
|
|
||||||
dec(FaRecord);
|
|
||||||
if (FaBlockIdx = FHeader^.firstBlock) and (FaRecord < 1) then
|
|
||||||
Result := grBOF
|
|
||||||
else
|
|
||||||
begin
|
begin
|
||||||
|
dec(FaRecord);
|
||||||
|
if (FaBlockIdx = FHeader^.firstBlock) and (FaRecord < 1) then
|
||||||
|
Result := grBOF
|
||||||
|
else
|
||||||
if FaRecord <= FaBlockStart then
|
if FaRecord <= FaBlockStart then
|
||||||
begin
|
begin
|
||||||
ReadPrevBlockHeader;
|
ReadPrevBlockHeader;
|
||||||
FaRecord := FaBlockStart+(FaBlock^.addDataSize div FHeader^.recordSize)+1;
|
FaRecord := FaBlockStart+(FaBlock^.addDataSize div FHeader^.recordSize)+1;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
gmCurrent:
|
||||||
|
if (FaRecord > RecordCount) or (FaRecord < 1) then
|
||||||
|
result := grError;
|
||||||
end;
|
end;
|
||||||
gmCurrent:
|
|
||||||
begin
|
if Result = grOK then begin
|
||||||
if (FaRecord > RecordCount) or (FaRecord < 1) then
|
if not FBlockReaded then
|
||||||
result := grError;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if Result = grOK then
|
|
||||||
begin
|
|
||||||
if not FBlockreaded then
|
|
||||||
ReadBlock;
|
ReadBlock;
|
||||||
L := ((faRecord-(FaBlockstart+1))*FHeader^.recordSize)+6;
|
L := ((faRecord - (FaBlockstart + 1))*FHeader^.recordSize) + 6;
|
||||||
if (faRecord-(FaBlockstart+1)) >= 0 then
|
if (faRecord - (FaBlockstart + 1)) >= 0 then
|
||||||
begin
|
Move(PChar(FaBlock)[L],Buffer[0],FHeader^.recordSize)
|
||||||
Move(PChar(FaBlock)[L],Buffer[0],FHeader^.recordSize);
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
result := grError;
|
Result := grError;
|
||||||
with PRecInfo(Buffer + FHeader^.recordSize)^ do
|
with PRecInfo(Buffer + FHeader^.recordSize)^ do begin
|
||||||
begin
|
BookmarkFlag := bfCurrent;
|
||||||
BookmarkFlag := bfCurrent;
|
RecordNumber := FaRecord;
|
||||||
RecordNumber := FaRecord;
|
end;
|
||||||
end;
|
|
||||||
|
// Filtering
|
||||||
|
if Filtered then
|
||||||
|
accepted := PXFilterRecord(Buffer)
|
||||||
|
else
|
||||||
|
accepted := True;
|
||||||
|
if (GetMode = gmCurrent) and not accepted then
|
||||||
|
Result := grError;
|
||||||
end;
|
end;
|
||||||
|
until (Result <> grOK) or Accepted;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TParadoxDataset.GetRecordCount: Integer;
|
function TParadoxDataset.GetRecordCount: Integer;
|
||||||
@ -584,6 +598,7 @@ begin
|
|||||||
DestroyFields;
|
DestroyFields;
|
||||||
FreeMem(FHeader);
|
FreeMem(FHeader);
|
||||||
FreeMem(FaBlock);
|
FreeMem(FaBlock);
|
||||||
|
FreeAndNil(FParser);
|
||||||
FreeAndNil(FBlobStream);
|
FreeAndNil(FBlobStream);
|
||||||
FreeAndNil(FStream);
|
FreeAndNil(FStream);
|
||||||
FActive := False;
|
FActive := False;
|
||||||
@ -730,6 +745,13 @@ begin
|
|||||||
if DefaultFields then CreateFields;
|
if DefaultFields then CreateFields;
|
||||||
BindFields(True);
|
BindFields(True);
|
||||||
FActive := True;
|
FActive := True;
|
||||||
|
|
||||||
|
try
|
||||||
|
ParseFilter(Filter);
|
||||||
|
except
|
||||||
|
on E: Exception do
|
||||||
|
Filter := '';
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TParadoxDataset.InternalPost;
|
procedure TParadoxDataset.InternalPost;
|
||||||
@ -737,9 +759,13 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TParadoxDataset.InternalSetToRecord(Buffer: PChar);
|
procedure TParadoxDataset.InternalSetToRecord(Buffer: PChar);
|
||||||
|
var
|
||||||
|
bm: LongWord;
|
||||||
begin
|
begin
|
||||||
if (State <> dsInsert) then
|
if (State <> dsInsert) then begin
|
||||||
InternalGotoBookmark(@PRecInfo(Buffer + FHeader^.RecordSize)^.RecordNumber);
|
bm := PRecInfo(Buffer + FHeader^.RecordSize)^.RecordNumber;
|
||||||
|
InternalGotoBookmark(@bm);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TParadoxDataset.IsCursorOpen: Boolean;
|
function TParadoxDataset.IsCursorOpen: Boolean;
|
||||||
@ -752,6 +778,59 @@ begin
|
|||||||
Result := not SameText(FTargetEncoding, EncodingUTF8);
|
Result := not SameText(FTargetEncoding, EncodingUTF8);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TParadoxDataset.ParseFilter(const AFilter: string);
|
||||||
|
begin
|
||||||
|
if Length(AFilter) > 0 then
|
||||||
|
begin
|
||||||
|
if (FParser = nil) and IsCursorOpen then
|
||||||
|
FParser := TBufDatasetParser.Create(Self);
|
||||||
|
if FParser <> nil then
|
||||||
|
begin
|
||||||
|
FParser.PartialMatch := not (foNoPartialCompare in FilterOptions);
|
||||||
|
FParser.CaseInsensitive := foCaseInsensitive in FilterOptions;
|
||||||
|
FParser.ParseExpression(AFilter);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TParadoxDataset.PxFilterRecord(Buffer: TRecordBuffer): Boolean;
|
||||||
|
var
|
||||||
|
SaveState: TDatasetState;
|
||||||
|
begin
|
||||||
|
Result := True;
|
||||||
|
if not Assigned(OnFilterRecord) and not Filtered then
|
||||||
|
Exit;
|
||||||
|
SaveState := SetTempState(dsFilter);
|
||||||
|
Try
|
||||||
|
FFilterBuffer := Buffer;
|
||||||
|
If Assigned(OnFilterRecord) then
|
||||||
|
OnFilterRecord(Self, Result);
|
||||||
|
If Result and Filtered and (Filter <> '') then
|
||||||
|
Result := Boolean((FParser.ExtractFromBuffer(FFilterBuffer))^);
|
||||||
|
Finally
|
||||||
|
RestoreState(SaveState);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TParadoxDataset.PxGetActiveBuffer(var Buffer: TRecordBuffer): Boolean;
|
||||||
|
begin
|
||||||
|
case State of
|
||||||
|
dsBrowse:
|
||||||
|
if IsEmpty then
|
||||||
|
Buffer := nil
|
||||||
|
else
|
||||||
|
Buffer := ActiveBuffer;
|
||||||
|
dsEdit,
|
||||||
|
dsInsert:
|
||||||
|
Buffer := ActiveBuffer;
|
||||||
|
dsFilter:
|
||||||
|
Buffer := FFilterBuffer;
|
||||||
|
else
|
||||||
|
Buffer := nil;
|
||||||
|
end;
|
||||||
|
Result := (Buffer <> nil);
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TParadoxDataset.ReadBlock;
|
procedure TParadoxDataset.ReadBlock;
|
||||||
var
|
var
|
||||||
L : longint;
|
L : longint;
|
||||||
@ -822,6 +901,25 @@ begin
|
|||||||
FFilename := AValue;
|
FFilename := AValue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TParadoxDataset.SetFiltered(Value: Boolean);
|
||||||
|
begin
|
||||||
|
if (Value <> Filtered) then begin
|
||||||
|
inherited;
|
||||||
|
if IsCursorOpen then
|
||||||
|
Refresh;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TParadoxDataset.SetFilterText(const Value: String);
|
||||||
|
begin
|
||||||
|
if (Value <> Filter) then begin
|
||||||
|
ParseFilter(Value);
|
||||||
|
inherited;
|
||||||
|
if IsCursorOpen and Filtered then
|
||||||
|
Refresh;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TParadoxDataset.SetRecNo(Value: Integer);
|
procedure TParadoxDataset.SetRecNo(Value: Integer);
|
||||||
begin
|
begin
|
||||||
if Value < FaRecord then
|
if Value < FaRecord then
|
||||||
|
Reference in New Issue
Block a user