|
@@ -46,29 +46,31 @@ type
|
|
|
|
|
|
MDSError=class(Exception);
|
|
MDSError=class(Exception);
|
|
|
|
|
|
- PRecInfo=^TMTRecInfo;
|
|
|
|
- TMTRecInfo=record
|
|
|
|
- Bookmark: Longint;
|
|
|
|
- BookmarkFlag: TBookmarkFlag;
|
|
|
|
- end;
|
|
|
|
-
|
|
|
|
{ TMemDataset }
|
|
{ TMemDataset }
|
|
|
|
|
|
TMemDataset=class(TDataSet)
|
|
TMemDataset=class(TDataSet)
|
|
private
|
|
private
|
|
- FOpenStream : TStream;
|
|
|
|
- FFileName : String;
|
|
|
|
- FFileModified : Boolean;
|
|
|
|
- FStream: TMemoryStream;
|
|
|
|
- FRecInfoOffset: integer;
|
|
|
|
- FRecCount: integer;
|
|
|
|
- FRecSize: integer;
|
|
|
|
- FCurrRecNo: integer;
|
|
|
|
- FIsOpen: boolean;
|
|
|
|
- FTableIsCreated: boolean;
|
|
|
|
- FFilterBuffer: TRecordBuffer;
|
|
|
|
- ffieldoffsets: PInteger;
|
|
|
|
- ffieldsizes: PInteger;
|
|
|
|
|
|
+ type
|
|
|
|
+ TMDSBlobList = class(TFPList)
|
|
|
|
+ public
|
|
|
|
+ procedure Clear; reintroduce;
|
|
|
|
+ end;
|
|
|
|
+ var
|
|
|
|
+ FOpenStream : TStream;
|
|
|
|
+ FFileName : String;
|
|
|
|
+ FFileModified : Boolean;
|
|
|
|
+ FStream: TMemoryStream;
|
|
|
|
+ FRecInfoOffset: integer;
|
|
|
|
+ FRecCount: integer;
|
|
|
|
+ FRecSize: integer;
|
|
|
|
+ FCurrRecNo: integer;
|
|
|
|
+ FIsOpen: boolean;
|
|
|
|
+ FTableIsCreated: boolean;
|
|
|
|
+ FFilterBuffer: TRecordBuffer;
|
|
|
|
+ ffieldoffsets: PInteger;
|
|
|
|
+ ffieldsizes: PInteger;
|
|
|
|
+ FBlobs: TMDSBlobList;
|
|
|
|
+
|
|
function GetRecordBufferPointer(p:TRecordBuffer; Pos:Integer):TRecordBuffer;
|
|
function GetRecordBufferPointer(p:TRecordBuffer; Pos:Integer):TRecordBuffer;
|
|
function GetIntegerPointer(p:PInteger; Pos:Integer):PInteger;
|
|
function GetIntegerPointer(p:PInteger; Pos:Integer):PInteger;
|
|
|
|
|
|
@@ -126,17 +128,16 @@ type
|
|
// If SaveData=False, a size 0 block should be written.
|
|
// If SaveData=False, a size 0 block should be written.
|
|
Procedure SaveDataToStream(F : TStream; SaveData : Boolean); virtual;
|
|
Procedure SaveDataToStream(F : TStream; SaveData : Boolean); virtual;
|
|
|
|
|
|
-
|
|
|
|
public
|
|
public
|
|
- constructor Create(AOwner:tComponent); override;
|
|
|
|
|
|
+ constructor Create(AOwner:TComponent); override;
|
|
destructor Destroy; override;
|
|
destructor Destroy; override;
|
|
function BookmarkValid(ABookmark: TBookmark): Boolean; override;
|
|
function BookmarkValid(ABookmark: TBookmark): Boolean; override;
|
|
|
|
+ function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; override;
|
|
function Locate(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions): 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;
|
|
function Lookup(const KeyFields: string; const KeyValues: Variant; const ResultFields: string): Variant; override;
|
|
- procedure CreateTable;
|
|
|
|
|
|
|
|
|
|
+ procedure CreateTable;
|
|
Function DataSize : Integer;
|
|
Function DataSize : Integer;
|
|
-
|
|
|
|
Procedure Clear(ClearDefs : Boolean);{$IFNDEF FPC} overload; {$ENDIF}
|
|
Procedure Clear(ClearDefs : Boolean);{$IFNDEF FPC} overload; {$ENDIF}
|
|
Procedure Clear;{$IFNDEF FPC} overload; {$ENDIF}
|
|
Procedure Clear;{$IFNDEF FPC} overload; {$ENDIF}
|
|
Procedure SaveToFile(AFileName : String);{$IFNDEF FPC} overload; {$ENDIF}
|
|
Procedure SaveToFile(AFileName : String);{$IFNDEF FPC} overload; {$ENDIF}
|
|
@@ -183,7 +184,7 @@ type
|
|
implementation
|
|
implementation
|
|
|
|
|
|
uses
|
|
uses
|
|
- Variants, FmtBCD;
|
|
|
|
|
|
+ DBConst, Variants, FmtBCD;
|
|
|
|
|
|
ResourceString
|
|
ResourceString
|
|
SErrFieldTypeNotSupported = 'Fieldtype of Field "%s" not supported.';
|
|
SErrFieldTypeNotSupported = 'Fieldtype of Field "%s" not supported.';
|
|
@@ -192,8 +193,40 @@ ResourceString
|
|
SErrInvalidMarkerAtPos = 'Wrong data stream marker at position %d. Got %d, expected %d';
|
|
SErrInvalidMarkerAtPos = 'Wrong data stream marker at position %d. Got %d, expected %d';
|
|
SErrNoFileName = 'Filename must not be empty.';
|
|
SErrNoFileName = 'Filename must not be empty.';
|
|
|
|
|
|
|
|
+type
|
|
|
|
+ TMDSRecInfo=record
|
|
|
|
+ Bookmark: Longint;
|
|
|
|
+ BookmarkFlag: TBookmarkFlag;
|
|
|
|
+ end;
|
|
|
|
+ PRecInfo=^TMDSRecInfo;
|
|
|
|
+
|
|
|
|
+ TMDSBlobField = record
|
|
|
|
+ Buffer: Pointer; // pointer to memory allocated for Blob data
|
|
|
|
+ Size: PtrInt; // size of Blob data
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ { TMDSBlobStream }
|
|
|
|
+
|
|
|
|
+ TMDSBlobStream = class(TStream)
|
|
|
|
+ private
|
|
|
|
+ FField : TBlobField;
|
|
|
|
+ FDataSet : TMemDataset;
|
|
|
|
+ FBlobField : TMDSBlobField;
|
|
|
|
+ FPosition : PtrInt;
|
|
|
|
+ FModified : boolean;
|
|
|
|
+ procedure AllocBlobField(NewSize: PtrInt);
|
|
|
|
+ procedure FreeBlobField;
|
|
|
|
+ public
|
|
|
|
+ constructor Create(Field: TField; Mode: TBlobStreamMode);
|
|
|
|
+ destructor Destroy; override;
|
|
|
|
+ function Seek(const Offset: int64; Origin: TSeekOrigin): int64; override;
|
|
|
|
+ function Read(var Buffer; Count: Longint): Longint; override;
|
|
|
|
+ function Write(const Buffer; Count: Longint): Longint; override;
|
|
|
|
+ end;
|
|
|
|
+
|
|
Const
|
|
Const
|
|
- SizeRecInfo = SizeOf(TMTRecInfo);
|
|
|
|
|
|
+ SizeRecInfo = SizeOf(TMDSRecInfo);
|
|
|
|
+
|
|
|
|
|
|
procedure unsetfieldisnull(nullmask: pbyte; const x: integer);
|
|
procedure unsetfieldisnull(nullmask: pbyte; const x: integer);
|
|
|
|
|
|
@@ -259,22 +292,110 @@ begin
|
|
S.WriteBuffer(Value[1],L);
|
|
S.WriteBuffer(Value[1],L);
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+
|
|
|
|
+{ TMDSBlobStream }
|
|
|
|
+
|
|
|
|
+constructor TMDSBlobStream.Create(Field: TField; Mode: TBlobStreamMode);
|
|
|
|
+begin
|
|
|
|
+ FField := Field as TBlobField;
|
|
|
|
+ FDataSet := Field.DataSet as TMemDataset;
|
|
|
|
+ if not Field.GetData(@FBlobField) then // IsNull
|
|
|
|
+ begin
|
|
|
|
+ FBlobField.Buffer := nil;
|
|
|
|
+ FBlobField.Size := 0;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ if Mode = bmWrite then
|
|
|
|
+ // release existing Blob
|
|
|
|
+ FreeBlobField;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+destructor TMDSBlobStream.Destroy;
|
|
|
|
+begin
|
|
|
|
+ if FModified then
|
|
|
|
+ begin
|
|
|
|
+ if FBlobField.Size = 0 then // Empty blob = IsNull
|
|
|
|
+ FField.SetData(nil)
|
|
|
|
+ else
|
|
|
|
+ FField.SetData(@FBlobField);
|
|
|
|
+ end;
|
|
|
|
+ inherited;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TMDSBlobStream.FreeBlobField;
|
|
|
|
+begin
|
|
|
|
+ FDataSet.FBlobs.Remove(FBlobField.Buffer);
|
|
|
|
+ FreeMem(FBlobField.Buffer, FBlobField.Size);
|
|
|
|
+ FBlobField.Buffer := nil;
|
|
|
|
+ FBlobField.Size := 0;
|
|
|
|
+ FModified := True;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TMDSBlobStream.AllocBlobField(NewSize: PtrInt);
|
|
|
|
+begin
|
|
|
|
+ FDataSet.FBlobs.Remove(FBlobField.Buffer);
|
|
|
|
+ ReAllocMem(FBlobField.Buffer, NewSize);
|
|
|
|
+ FDataSet.FBlobs.Add(FBlobField.Buffer);
|
|
|
|
+ FModified := True;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TMDSBlobStream.Seek(const Offset: int64; Origin: TSeekOrigin): int64;
|
|
|
|
+begin
|
|
|
|
+ Case Origin of
|
|
|
|
+ soBeginning : FPosition := Offset;
|
|
|
|
+ soEnd : FPosition := FBlobField.Size + Offset;
|
|
|
|
+ soCurrent : FPosition := FPosition + Offset;
|
|
|
|
+ end;
|
|
|
|
+ Result := FPosition;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TMDSBlobStream.Read(var Buffer; Count: Longint): Longint;
|
|
|
|
+var p: Pointer;
|
|
|
|
+begin
|
|
|
|
+ if FPosition + Count > FBlobField.Size then
|
|
|
|
+ Count := FBlobField.Size - FPosition;
|
|
|
|
+ p := FBlobField.Buffer + FPosition;
|
|
|
|
+ Move(p^, Buffer, Count);
|
|
|
|
+ Inc(FPosition, Count);
|
|
|
|
+ Result := Count;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TMDSBlobStream.Write(const Buffer; Count: Longint): Longint;
|
|
|
|
+var p: Pointer;
|
|
|
|
+begin
|
|
|
|
+ AllocBlobField(FPosition+Count);
|
|
|
|
+ p := FBlobField.Buffer + FPosition;
|
|
|
|
+ Move(Buffer, p^, Count);
|
|
|
|
+ Inc(FBlobField.Size, Count);
|
|
|
|
+ Inc(FPosition, Count);
|
|
|
|
+ Result := Count;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+{ TMemDataset.TMDSBlobList }
|
|
|
|
+
|
|
|
|
+procedure TMemDataset.TMDSBlobList.Clear;
|
|
|
|
+var i: integer;
|
|
|
|
+begin
|
|
|
|
+ for i:=0 to Count-1 do FreeMem(Items[i]);
|
|
|
|
+ inherited Clear;
|
|
|
|
+end;
|
|
|
|
+
|
|
{ ---------------------------------------------------------------------
|
|
{ ---------------------------------------------------------------------
|
|
TMemDataset
|
|
TMemDataset
|
|
---------------------------------------------------------------------}
|
|
---------------------------------------------------------------------}
|
|
|
|
|
|
-
|
|
|
|
-constructor TMemDataset.Create(AOwner:tComponent);
|
|
|
|
|
|
+constructor TMemDataset.Create(AOwner:TComponent);
|
|
|
|
|
|
begin
|
|
begin
|
|
- inherited create(aOwner);
|
|
|
|
|
|
+ inherited Create(AOwner);
|
|
FStream:=TMemoryStream.Create;
|
|
FStream:=TMemoryStream.Create;
|
|
FRecCount:=0;
|
|
FRecCount:=0;
|
|
FRecSize:=0;
|
|
FRecSize:=0;
|
|
FRecInfoOffset:=0;
|
|
FRecInfoOffset:=0;
|
|
FCurrRecNo:=-1;
|
|
FCurrRecNo:=-1;
|
|
BookmarkSize := sizeof(Longint);
|
|
BookmarkSize := sizeof(Longint);
|
|
- FIsOpen:=False;
|
|
|
|
|
|
+ FBlobs := TMDSBlobList.Create;
|
|
end;
|
|
end;
|
|
|
|
|
|
destructor TMemDataset.Destroy;
|
|
destructor TMemDataset.Destroy;
|
|
@@ -282,6 +403,8 @@ begin
|
|
FStream.Free;
|
|
FStream.Free;
|
|
FreeMem(FFieldOffsets);
|
|
FreeMem(FFieldOffsets);
|
|
FreeMem(FFieldSizes);
|
|
FreeMem(FFieldSizes);
|
|
|
|
+ FBlobs.Clear;
|
|
|
|
+ FBlobs.Free;
|
|
inherited Destroy;
|
|
inherited Destroy;
|
|
end;
|
|
end;
|
|
|
|
|
|
@@ -295,6 +418,20 @@ begin
|
|
Result := (ReqBookmark>=0) and (ReqBookmark<FRecCount);
|
|
Result := (ReqBookmark>=0) and (ReqBookmark<FRecCount);
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+function TMemDataset.CreateBlobStream(Field: TField; Mode: TBlobStreamMode
|
|
|
|
+ ): TStream;
|
|
|
|
+begin
|
|
|
|
+ // Blobs are not saved to stream/file !
|
|
|
|
+ if Mode = bmWrite then
|
|
|
|
+ begin
|
|
|
|
+ if not (State in [dsEdit, dsInsert, dsFilter, dsCalcFields]) then
|
|
|
|
+ DatabaseErrorFmt(SNotEditing, [Name], Self);
|
|
|
|
+ if Field.ReadOnly and not (State in [dsSetKey, dsFilter]) then
|
|
|
|
+ DatabaseErrorFmt(SReadOnlyField, [Field.DisplayName]);
|
|
|
|
+ end;
|
|
|
|
+ Result := TMDSBlobStream.Create(Field, Mode);
|
|
|
|
+end;
|
|
|
|
+
|
|
function TMemDataset.MDSGetRecordOffset(ARecNo: integer): longint;
|
|
function TMemDataset.MDSGetRecordOffset(ARecNo: integer): longint;
|
|
begin
|
|
begin
|
|
Result:=FRecSize*ARecNo
|
|
Result:=FRecSize*ARecNo
|
|
@@ -302,7 +439,7 @@ end;
|
|
|
|
|
|
function TMemDataset.MDSGetFieldOffset(FieldNo: integer): integer;
|
|
function TMemDataset.MDSGetFieldOffset(FieldNo: integer): integer;
|
|
begin
|
|
begin
|
|
- result:= getIntegerpointer(ffieldoffsets, fieldno-1)^;
|
|
|
|
|
|
+ Result:= getIntegerPointer(ffieldoffsets, fieldno-1)^;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TMemDataset.RaiseError(Fmt: String; Args: array of const);
|
|
procedure TMemDataset.RaiseError(Fmt: String; Args: array of const);
|
|
@@ -333,10 +470,12 @@ begin
|
|
ftTime,
|
|
ftTime,
|
|
ftDate: result:=SizeOf(TDateTime);
|
|
ftDate: result:=SizeOf(TDateTime);
|
|
ftFmtBCD: result:=SizeOf(TBCD);
|
|
ftFmtBCD: result:=SizeOf(TBCD);
|
|
- ftWideString,
|
|
|
|
- ftFixedWideChar: result:=(FD.Size+1)*SizeOf(WideChar);
|
|
|
|
|
|
+ ftWideString, ftFixedWideChar:
|
|
|
|
+ result:=(FD.Size+1)*SizeOf(WideChar);
|
|
ftBytes: result := FD.Size;
|
|
ftBytes: result := FD.Size;
|
|
ftVarBytes: result := FD.Size + SizeOf(Word);
|
|
ftVarBytes: result := FD.Size + SizeOf(Word);
|
|
|
|
+ ftBlob, ftMemo, ftWideMemo:
|
|
|
|
+ result := SizeOf(TMDSBlobField);
|
|
else
|
|
else
|
|
RaiseError(SErrFieldTypeNotSupported,[FD.Name]);
|
|
RaiseError(SErrFieldTypeNotSupported,[FD.Name]);
|
|
end;
|
|
end;
|
|
@@ -533,6 +672,7 @@ Var
|
|
begin
|
|
begin
|
|
CheckMarker(F,smData);
|
|
CheckMarker(F,smData);
|
|
Size:=ReadInteger(F);
|
|
Size:=ReadInteger(F);
|
|
|
|
+ FBlobs.Clear;
|
|
FStream.Clear;
|
|
FStream.Clear;
|
|
FStream.CopyFrom(F,Size);
|
|
FStream.CopyFrom(F,Size);
|
|
FRecCount:=Size div FRecSize;
|
|
FRecCount:=Size div FRecSize;
|
|
@@ -654,9 +794,8 @@ begin
|
|
FIsOpen:=False;
|
|
FIsOpen:=False;
|
|
FFileModified:=False;
|
|
FFileModified:=False;
|
|
// BindFields(False);
|
|
// BindFields(False);
|
|
- if DefaultFields then begin
|
|
|
|
|
|
+ if DefaultFields then
|
|
DestroyFields;
|
|
DestroyFields;
|
|
- end;
|
|
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TMemDataset.InternalPost;
|
|
procedure TMemDataset.InternalPost;
|
|
@@ -872,6 +1011,7 @@ end;
|
|
procedure TMemDataset.Clear(ClearDefs : Boolean);
|
|
procedure TMemDataset.Clear(ClearDefs : Boolean);
|
|
|
|
|
|
begin
|
|
begin
|
|
|
|
+ FBlobs.Clear;
|
|
FStream.Clear;
|
|
FStream.Clear;
|
|
FRecCount:=0;
|
|
FRecCount:=0;
|
|
FCurrRecNo:=-1;
|
|
FCurrRecNo:=-1;
|
|
@@ -907,7 +1047,7 @@ begin
|
|
for i:= 0 to Count-1 do
|
|
for i:= 0 to Count-1 do
|
|
begin
|
|
begin
|
|
GetIntegerPointer(FFieldOffsets, i)^ := FRecSize;
|
|
GetIntegerPointer(FFieldOffsets, i)^ := FRecSize;
|
|
- GetIntegerPointer(FFieldSizes, i)^ := MDSGetbufferSize(i+1);
|
|
|
|
|
|
+ GetIntegerPointer(FFieldSizes, i)^ := MDSGetBufferSize(i+1);
|
|
FRecSize:= FRecSize+GetIntegerPointer(FFieldSizes, i)^;
|
|
FRecSize:= FRecSize+GetIntegerPointer(FFieldSizes, i)^;
|
|
end;
|
|
end;
|
|
FRecInfoOffset:=FRecSize;
|
|
FRecInfoOffset:=FRecSize;
|
|
@@ -918,10 +1058,7 @@ procedure TMemDataset.CreateTable;
|
|
|
|
|
|
begin
|
|
begin
|
|
CheckInactive;
|
|
CheckInactive;
|
|
- FStream.Clear;
|
|
|
|
- FRecCount:=0;
|
|
|
|
- FCurrRecNo:=-1;
|
|
|
|
- FIsOpen:=False;
|
|
|
|
|
|
+ Clear(False);
|
|
calcrecordlayout;
|
|
calcrecordlayout;
|
|
FTableIsCreated:=True;
|
|
FTableIsCreated:=True;
|
|
end;
|
|
end;
|