548 lines
14 KiB
ObjectPascal
548 lines
14 KiB
ObjectPascal
{-----------------------------------------------------------------------------
|
|
The contents of this file are subject to the Mozilla Public License
|
|
Version 1.1 (the "License"); you may not use this file except in compliance
|
|
with the License. You may obtain a copy of the License at
|
|
http://www.mozilla.org/MPL/MPL-1.1.html
|
|
|
|
Software distributed under the License is distributed on an "AS IS" basis,
|
|
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
|
|
the specific language governing rights and limitations under the License.
|
|
|
|
The Original Code is: JvMemTable.PAS, released on 2002-07-04.
|
|
|
|
The Initial Developers of the Original Code are: Fedor Koshevnikov, Igor Pavluk and Serge Korolev
|
|
Copyright (c) 1997, 1998 Fedor Koshevnikov, Igor Pavluk and Serge Korolev
|
|
Copyright (c) 2001,2002 SGB Software
|
|
All Rights Reserved.
|
|
|
|
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
|
|
located at http://jvcl.sourceforge.net
|
|
|
|
Known Issues:
|
|
-----------------------------------------------------------------------------}
|
|
// $Id: JvBDEMemTable.pas 10612 2006-05-19 19:04:09Z jfudickar $
|
|
|
|
unit JvBDEMemTable;
|
|
|
|
{$I jvcl.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
{$IFDEF UNITVERSIONING}
|
|
JclUnitVersioning,
|
|
{$ENDIF UNITVERSIONING}
|
|
SysUtils, Classes,
|
|
BDE, DB, DBTables;
|
|
|
|
type
|
|
TJvBDEMemoryTable = class(TDBDataSet)
|
|
private
|
|
FTableName: TFileName;
|
|
FMoveHandle: HDBICur;
|
|
FEnableDelete: Boolean;
|
|
FDisableEvents: Boolean;
|
|
procedure EncodeFieldDesc(var FieldDesc: FLDDesc;
|
|
const Name: string; DataType: TFieldType; Size, Precision: Word);
|
|
procedure SetTableName(const Value: TFileName);
|
|
function SupportedFieldType(AType: TFieldType): Boolean;
|
|
procedure DeleteCurrentRecord;
|
|
protected
|
|
function CreateHandle: HDBICur; override;
|
|
procedure DoBeforeClose; override;
|
|
procedure DoAfterClose; override;
|
|
procedure DoBeforeOpen; override;
|
|
procedure DoAfterOpen; override;
|
|
procedure DoBeforeScroll; override;
|
|
procedure DoAfterScroll; override;
|
|
function GetRecordCount: Integer; override;
|
|
function GetRecNo: Integer; override;
|
|
procedure SetRecNo(Value: Integer); override;
|
|
procedure InternalDelete; override;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
function BatchMove(ASource: TDataSet; AMode: TBatchMode;
|
|
ARecordCount: Longint): Longint;
|
|
procedure CopyStructure(ASource: TDataSet);
|
|
procedure CreateTable;
|
|
procedure DeleteTable;
|
|
procedure EmptyTable;
|
|
procedure GotoRecord(RecordNo: Longint);
|
|
function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
|
|
function IsSequenced: Boolean; override;
|
|
function Locate(const KeyFields: string; const KeyValues: Variant;
|
|
Options: TLocateOptions): Boolean; override;
|
|
function Lookup(const KeyFields: string; const KeyValues: Variant;
|
|
const ResultFields: string): Variant; override;
|
|
procedure SetFieldValues(const FieldNames: array of string;
|
|
const Values: array of const);
|
|
published
|
|
property EnableDelete: Boolean read FEnableDelete write FEnableDelete default True;
|
|
property TableName: TFileName read FTableName write SetTableName;
|
|
end;
|
|
|
|
{$IFDEF UNITVERSIONING}
|
|
const
|
|
UnitVersioning: TUnitVersionInfo = (
|
|
RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/tags/JVCL3_32/run/JvBDEMemTable.pas $';
|
|
Revision: '$Revision: 10612 $';
|
|
Date: '$Date: 2006-05-19 21:04:09 +0200 (ven., 19 mai 2006) $';
|
|
LogPath: 'JVCL\run'
|
|
);
|
|
{$ENDIF UNITVERSIONING}
|
|
|
|
implementation
|
|
|
|
uses
|
|
BDEConst, DBConsts, Math,
|
|
JvDBUtils, JvBdeUtils;
|
|
|
|
const
|
|
InternalMemTableName1 = '$InMem$';
|
|
InternalMemTableName2 = '$JvInMem$';
|
|
|
|
{ Memory tables are created in RAM and deleted when you close them. They
|
|
are much faster and are very useful when you need fast operations on
|
|
small tables. Memory tables do not support certain features (like
|
|
deleting records, referntial integrity, indexes, autoincrement fields
|
|
and BLOBs) }
|
|
|
|
constructor TJvBDEMemoryTable.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
FEnableDelete := True;
|
|
end;
|
|
|
|
function TJvBDEMemoryTable.BatchMove(ASource: TDataSet; AMode: TBatchMode;
|
|
ARecordCount: Longint): Longint;
|
|
var
|
|
SourceActive: Boolean;
|
|
MovedCount: Longint;
|
|
begin
|
|
if (ASource = nil) or (Self = ASource) or
|
|
not (AMode in [batCopy, batAppend]) then
|
|
_DBError(SInvalidBatchMove);
|
|
SourceActive := ASource.Active;
|
|
try
|
|
ASource.DisableControls;
|
|
DisableControls;
|
|
ASource.Open;
|
|
ASource.CheckBrowseMode;
|
|
ASource.UpdateCursorPos;
|
|
if AMode = batCopy then
|
|
begin
|
|
Close;
|
|
CopyStructure(ASource);
|
|
end;
|
|
if not Active then
|
|
Open;
|
|
CheckBrowseMode;
|
|
if ARecordCount > 0 then
|
|
begin
|
|
ASource.UpdateCursorPos;
|
|
MovedCount := ARecordCount;
|
|
end
|
|
else
|
|
begin
|
|
ASource.First;
|
|
MovedCount := MaxLongint;
|
|
end;
|
|
try
|
|
Result := 0;
|
|
while not ASource.Eof do
|
|
begin
|
|
Append;
|
|
AssignRecord(ASource, Self, True);
|
|
Post;
|
|
Inc(Result);
|
|
if Result >= MovedCount then
|
|
Break;
|
|
ASource.Next;
|
|
end;
|
|
finally
|
|
Self.First;
|
|
end;
|
|
finally
|
|
if not SourceActive then
|
|
ASource.Close;
|
|
Self.EnableControls;
|
|
ASource.EnableControls;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvBDEMemoryTable.CopyStructure(ASource: TDataSet);
|
|
var
|
|
I: Integer;
|
|
|
|
procedure CreateField(FieldDef: TFieldDef; AOwner: TComponent);
|
|
begin
|
|
FieldDef.CreateField(AOwner, nil, FieldDef.Name, True);
|
|
end;
|
|
|
|
begin
|
|
CheckInactive;
|
|
for I := FieldCount - 1 downto 0 do
|
|
Fields[I].Free;
|
|
if ASource = nil then
|
|
Exit;
|
|
ASource.FieldDefs.Update;
|
|
FieldDefs := ASource.FieldDefs;
|
|
for I := 0 to FieldDefs.Count - 1 do
|
|
if SupportedFieldType(FieldDefs.Items[I].DataType) then
|
|
begin
|
|
if (csDesigning in ComponentState) and (Owner <> nil) then
|
|
CreateField(FieldDefs.Items[I], Owner)
|
|
else
|
|
CreateField(FieldDefs.Items[I], Self);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvBDEMemoryTable.DeleteCurrentRecord;
|
|
var
|
|
CurRecNo, CurRec: Longint;
|
|
Buffer: Pointer;
|
|
iFldCount: Word;
|
|
FieldDescs: PFLDDesc;
|
|
begin
|
|
CurRecNo := RecNo;
|
|
iFldCount := FieldDefs.Count;
|
|
FieldDescs := AllocMem(iFldCount * SizeOf(FLDDesc));
|
|
try
|
|
Check(DbiGetFieldDescs(Handle, FieldDescs));
|
|
Check(DbiCreateInMemTable(DBHandle, InternalMemTableName1, iFldCount, FieldDescs,
|
|
FMoveHandle));
|
|
try
|
|
DisableControls;
|
|
Buffer := AllocMem(RecordSize);
|
|
try
|
|
First;
|
|
CurRec := 0;
|
|
while not Self.Eof do
|
|
begin
|
|
Inc(CurRec);
|
|
if CurRec <> CurRecNo then
|
|
begin
|
|
DbiInitRecord(FMoveHandle, Buffer);
|
|
Self.GetCurrentRecord(Buffer);
|
|
Check(DbiAppendRecord(FMoveHandle, Buffer));
|
|
end;
|
|
Self.Next;
|
|
end;
|
|
FDisableEvents := True;
|
|
try
|
|
Close;
|
|
Open;
|
|
FMoveHandle := nil;
|
|
finally
|
|
FDisableEvents := False;
|
|
end;
|
|
finally
|
|
FreeMem(Buffer, RecordSize);
|
|
end;
|
|
except
|
|
DbiCloseCursor(FMoveHandle);
|
|
FMoveHandle := nil;
|
|
raise;
|
|
end;
|
|
GotoRecord(CurRecNo - 1);
|
|
finally
|
|
if FieldDescs <> nil then
|
|
FreeMem(FieldDescs, iFldCount * SizeOf(FLDDesc));
|
|
FMoveHandle := nil;
|
|
EnableControls;
|
|
end;
|
|
end;
|
|
|
|
|
|
function TJvBDEMemoryTable.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
|
|
var
|
|
IsBlank: LongBool;
|
|
RecBuf: PChar;
|
|
begin
|
|
Result := inherited GetFieldData(Field, Buffer);
|
|
if not Result then
|
|
begin
|
|
RecBuf := nil;
|
|
case State of
|
|
dsBrowse:
|
|
if not IsEmpty then
|
|
RecBuf := ActiveBuffer;
|
|
dsEdit, dsInsert:
|
|
RecBuf := ActiveBuffer;
|
|
dsCalcFields:
|
|
RecBuf := CalcBuffer;
|
|
end;
|
|
if RecBuf = nil then
|
|
Exit;
|
|
with Field do
|
|
if FieldNo > 0 then
|
|
begin
|
|
Check(DbiGetField(Handle, FieldNo, RecBuf, nil, IsBlank));
|
|
Result := not IsBlank;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvBDEMemoryTable.InternalDelete;
|
|
begin
|
|
if EnableDelete then
|
|
DeleteCurrentRecord
|
|
else
|
|
inherited InternalDelete;
|
|
end;
|
|
|
|
function TJvBDEMemoryTable.Locate(const KeyFields: string;
|
|
const KeyValues: Variant; Options: TLocateOptions): Boolean;
|
|
begin
|
|
DoBeforeScroll;
|
|
Result := DataSetLocateThrough(Self, KeyFields, KeyValues, Options);
|
|
if Result then
|
|
begin
|
|
DataEvent(deDataSetChange, 0);
|
|
DoAfterScroll;
|
|
end;
|
|
end;
|
|
|
|
function TJvBDEMemoryTable.Lookup(const KeyFields: string;
|
|
const KeyValues: Variant; const ResultFields: string): Variant;
|
|
begin
|
|
Result := False;
|
|
end;
|
|
|
|
|
|
procedure TJvBDEMemoryTable.DoAfterClose;
|
|
begin
|
|
if not FDisableEvents then
|
|
inherited DoAfterClose;
|
|
end;
|
|
|
|
procedure TJvBDEMemoryTable.DoAfterOpen;
|
|
begin
|
|
if not FDisableEvents then
|
|
inherited DoAfterOpen;
|
|
end;
|
|
|
|
procedure TJvBDEMemoryTable.DoBeforeClose;
|
|
begin
|
|
if not FDisableEvents then
|
|
inherited DoBeforeClose;
|
|
end;
|
|
|
|
procedure TJvBDEMemoryTable.DoBeforeOpen;
|
|
begin
|
|
if not FDisableEvents then
|
|
inherited DoBeforeOpen;
|
|
end;
|
|
|
|
|
|
procedure TJvBDEMemoryTable.DoBeforeScroll;
|
|
begin
|
|
if not FDisableEvents then
|
|
inherited DoBeforeScroll;
|
|
end;
|
|
|
|
procedure TJvBDEMemoryTable.DoAfterScroll;
|
|
begin
|
|
if not FDisableEvents then
|
|
inherited DoAfterScroll;
|
|
end;
|
|
|
|
function TJvBDEMemoryTable.SupportedFieldType(AType: TFieldType): Boolean;
|
|
begin
|
|
Result := not (AType in [ftUnknown, ftWideString, ftOraBlob, ftOraClob, ftVariant, ftInterface,
|
|
ftIDispatch, ftGuid] + ftNonTextTypes);
|
|
end;
|
|
|
|
function TJvBDEMemoryTable.CreateHandle: HDBICur;
|
|
var
|
|
I: Integer;
|
|
FldDescList: TFieldDescList;
|
|
FieldDescs: PFLDDesc;
|
|
iFldCount: Cardinal;
|
|
szTblName: DBITBLNAME;
|
|
begin
|
|
if (FMoveHandle <> nil) then
|
|
begin
|
|
Result := FMoveHandle;
|
|
Exit;
|
|
end;
|
|
if FieldCount > 0 then
|
|
FieldDefs.Clear;
|
|
if FieldDefs.Count = 0 then
|
|
for I := 0 to FieldCount - 1 do
|
|
begin
|
|
if not SupportedFieldType(Fields[I].DataType) then
|
|
DatabaseErrorFmt(SUnknownFieldType, [Fields[I].FieldName]);
|
|
with Fields[I] do
|
|
if not (Calculated or Lookup) then
|
|
FieldDefs.Add(FieldName, DataType, Size, Required);
|
|
end;
|
|
iFldCount := FieldDefs.Count;
|
|
SetDBFlag(dbfTable, True);
|
|
try
|
|
if TableName = '' then
|
|
AnsiToNative(Locale, InternalMemTableName1, szTblName, SizeOf(szTblName) - 1)
|
|
else
|
|
AnsiToNative(Locale, TableName, szTblName, SizeOf(szTblName) - 1);
|
|
SetLength(FldDescList, iFldCount);
|
|
FieldDescs := BDE.PFLDDesc(FldDescList);
|
|
for I := 0 to FieldDefs.Count - 1 do
|
|
with FieldDefs[I] do
|
|
EncodeFieldDesc(FldDescList[I], Name, DataType, Size, Precision);
|
|
Check(DbiTranslateRecordStructure(nil, iFldCount, FieldDescs, nil, nil,
|
|
FieldDescs, False));
|
|
Check(DbiCreateInMemTable(DBHandle, szTblName, iFldCount, FieldDescs,
|
|
Result));
|
|
finally
|
|
SetDBFlag(dbfTable, False);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvBDEMemoryTable.CreateTable;
|
|
begin
|
|
CheckInactive;
|
|
Open;
|
|
end;
|
|
|
|
procedure TJvBDEMemoryTable.DeleteTable;
|
|
begin
|
|
CheckBrowseMode;
|
|
Close;
|
|
end;
|
|
|
|
procedure TJvBDEMemoryTable.EmptyTable;
|
|
begin
|
|
if Active then
|
|
begin
|
|
CheckBrowseMode;
|
|
DisableControls;
|
|
FDisableEvents := True;
|
|
try
|
|
Close;
|
|
Open;
|
|
finally
|
|
FDisableEvents := False;
|
|
EnableControls;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvBDEMemoryTable.EncodeFieldDesc(var FieldDesc: FLDDesc;
|
|
const Name: string; DataType: TFieldType; Size, Precision: Word);
|
|
begin
|
|
with FieldDesc do
|
|
begin
|
|
FillChar(szName, SizeOf(szName), 0);
|
|
AnsiToNative(Locale, Name, szName, SizeOf(szName) - 1);
|
|
iFldType := FieldLogicMap(DataType);
|
|
iSubType := FieldSubtypeMap(DataType);
|
|
if iSubType = fldstAUTOINC then
|
|
iSubType := 0;
|
|
case DataType of
|
|
ftString, ftFixedChar, ftBytes, ftVarBytes, ftBlob..ftTypedBinary:
|
|
iUnits1 := Size;
|
|
ftBCD:
|
|
begin
|
|
{ Default precision is 32, Size = Scale }
|
|
if (Precision > 0) and (Precision <= 32) then
|
|
iUnits1 := Precision
|
|
else
|
|
iUnits1 := 32;
|
|
iUnits2 := Size; {Scale}
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TJvBDEMemoryTable.GetRecordCount: Integer;
|
|
begin
|
|
if State = dsInactive then
|
|
_DBError(SDataSetClosed);
|
|
Check(DbiGetRecordCount(Handle, Result));
|
|
end;
|
|
|
|
procedure TJvBDEMemoryTable.SetRecNo(Value: Integer);
|
|
var
|
|
Rslt: DBIResult;
|
|
begin
|
|
CheckBrowseMode;
|
|
UpdateCursorPos;
|
|
Rslt := DbiSetToSeqNo(Handle, Value);
|
|
if Rslt = DBIERR_EOF then
|
|
Last
|
|
else
|
|
if Rslt = DBIERR_BOF then
|
|
First
|
|
else
|
|
begin
|
|
Check(Rslt);
|
|
Resync([rmExact, rmCenter]);
|
|
end;
|
|
end;
|
|
|
|
function TJvBDEMemoryTable.GetRecNo: Integer;
|
|
var
|
|
Rslt: DBIResult;
|
|
begin
|
|
Result := -1;
|
|
if State in [dsBrowse, dsEdit] then
|
|
begin
|
|
UpdateCursorPos;
|
|
Rslt := DbiGetSeqNo(Handle, Result);
|
|
if (Rslt <> DBIERR_EOF) and (Rslt <> DBIERR_BOF) then
|
|
Check(Rslt);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvBDEMemoryTable.GotoRecord(RecordNo: Longint);
|
|
begin
|
|
RecNo := RecordNo;
|
|
end;
|
|
|
|
function TJvBDEMemoryTable.IsSequenced: Boolean;
|
|
begin
|
|
Result := not Filtered;
|
|
end;
|
|
|
|
procedure TJvBDEMemoryTable.SetFieldValues(const FieldNames: array of string;
|
|
const Values: array of const);
|
|
var
|
|
I: Integer;
|
|
Pos: Longint;
|
|
begin
|
|
Pos := RecNo;
|
|
DisableControls;
|
|
try
|
|
First;
|
|
while not Eof do
|
|
begin
|
|
Edit;
|
|
for I := 0 to Max(High(FieldNames), High(Values)) do
|
|
FieldByName(FieldNames[I]).AssignValue(Values[I]);
|
|
Post;
|
|
Next;
|
|
end;
|
|
GotoRecord(Pos);
|
|
finally
|
|
EnableControls;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvBDEMemoryTable.SetTableName(const Value: TFileName);
|
|
begin
|
|
CheckInactive;
|
|
FTableName := Value;
|
|
DataEvent(dePropertyChange, 0);
|
|
end;
|
|
|
|
{$IFDEF UNITVERSIONING}
|
|
initialization
|
|
RegisterUnitVersion(HInstance, UnitVersioning);
|
|
|
|
finalization
|
|
UnregisterUnitVersion(HInstance);
|
|
{$ENDIF UNITVERSIONING}
|
|
|
|
end.
|
|
|