Componentes.Terceros.SDAC/internal/4.10.0.10/1/Source/MemDS.pas
2007-10-05 14:48:18 +00:00

3922 lines
114 KiB
ObjectPascal

//////////////////////////////////////////////////
// DB Access Components
// Copyright © 1998-2007 Core Lab. All right reserved.
// Memory Data Set
// Created: 01.02.98
//////////////////////////////////////////////////
{$IFNDEF CLR}
{$I Dac.inc}
unit MemDS;
{$ENDIF}
interface
uses
{$IFDEF CLR}
DB, SysUtils, Classes, MemData, MemUtils{$IFDEF BDE_SHARED}, DBTables{$ENDIF}, Variants,
System.Runtime.InteropServices, System.Text, System.IO, System.XML;
{$ELSE}
DB, SysUtils, Classes, MemData, MemUtils{$IFDEF BDE_SHARED}, DBTables{$ENDIF}
{$IFDEF VER6P}, Variants{$ENDIF}, CRXml, CLRClasses;
{$ENDIF}
const
uaDefault = 10; // TUpdateAction
type
{ TMemDataSet }
{$IFDEF VER4}
{$IFNDEF BDE_SHARED}
TUpdateAction = (uaFail, uaAbort, uaSkip, uaRetry, uaApplied); // uaDefault
{$ENDIF}
{$ENDIF}
TUpdateRecordTypes = set of (rtModified, rtInserted, rtDeleted, rtUnmodified);
TUpdateErrorEvent = procedure(DataSet: TDataSet; E: EDatabaseError;
UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction) of object;
TUpdateRecordEvent = procedure(DataSet: TDataSet; UpdateKind: TUpdateKind;
var UpdateAction: TUpdateAction) of object;
{$IFDEF CLR}
PRecInfo = packed record
private
Ptr: IntPtr;
function GetRecordNumber: longint;
procedure SetRecordNumber(const Value: longint);
function GetUpdateStatus: TUpdateStatus;
procedure SetUpdateStatus(const Value: TUpdateStatus);
function GetBookmarkFlag: TBookmarkFlag;
procedure SetBookmarkFlag(const Value: TBookmarkFlag);
function GetRefComplexFields: boolean;
procedure SetRefComplexFields(const Value: boolean);
public
property RecordNumber: longint read GetRecordNumber write SetRecordNumber;
property UpdateStatus: TUpdateStatus read GetUpdateStatus write SetUpdateStatus;
property BookmarkFlag: TBookmarkFlag read GetBookmarkFlag write SetBookmarkFlag;
property RefComplexFields: boolean read GetRefComplexFields write SetRefComplexFields;
class operator Implicit(AValue: IntPtr): PRecInfo;
class operator Implicit(AValue: integer): PRecInfo;
end;
{$ELSE}
TRecordBuffer = PChar;
TValueBuffer = pointer;
PRecInfo = ^TRecInfo;
{$ENDIF}
TRecInfo = packed record
RecordNumber: longint;
UpdateStatus: TUpdateStatus;
BookmarkFlag: TBookmarkFlag;
RefComplexFields: boolean;
end;
// TBlobData = string;
TCalcFieldDescMapping = record
FieldDesc: TFieldDesc;
Field: TField;
end;
TMemDataSet = class(TDataSet)
private
FOldRecBuf: TRecordBuffer;
FFilterBuffer: TRecordBuffer;
FCachedUpdates: boolean;
FLocalUpdate: boolean;
//FInDeferredPost: boolean; to protected
FInInserting: boolean;
FInEditing: boolean;
FIndexFieldNames: string;
FCalcFieldsMapping: array of TCalcFieldDescMapping;
FOnUpdateError: TUpdateErrorEvent;
FOnUpdateRecord: TUpdateRecordEvent;
//Renamed LocateRecord (CBuilder5 bug - overloaded methods in different sections):
function InternalLocateRecord(KeyFields: TDAList; const KeyValues: variant;
Options: TLocateExOptions; SavePos: boolean): boolean; overload;
{function GetBlobData(Field:TField; Buffer: PChar):TBlobData;
procedure SetBlobData(Field:TField; Buffer: PChar; Value:TBlobData);
procedure ClearBlobCache(Buffer: PChar);}
procedure SetCachedUpdates(Value: boolean);
function GetUpdatesPending: boolean;
function GetPrepared: boolean;
procedure SetPrepared(Value: boolean);
function GetUpdateRecordSet: TUpdateRecordTypes;
procedure SetUpdateRecordSet(Value: TUpdateRecordTypes);
procedure SetIndexFieldNames(Value: string);
protected
Data: TData; // FIRecordSet
FBookmarkOfs: longint;
FRecInfoOfs: longint;
{$IFNDEF CLR}
{$IFNDEF VER10P}
FWideStringOfs: longint;
{$ENDIF}
{$ENDIF}
FRecBufSize: longint;
FInCacheProcessing: boolean;
FInDeferredPost: boolean; // private
NewCacheRecBuf: TRecordBuffer;
OldCacheRecBuf: TRecordBuffer;
OldDeferredPostBuf: TRecordBuffer;
FParentDataSet: TMemDataSet;
FLastParentPos: integer;
FLocalConstraints: boolean;
FNumberRange: boolean;
FNeedAddRef: boolean;
FCacheCalcFields: boolean;
FCreateCalcFieldDescs: boolean;
procedure CreateIRecordSet; virtual;
procedure FreeIRecordSet;
procedure SetIRecordSet(Value: TData{TRecordSet}); virtual;
{ Open/Close DataSet }
procedure OpenCursor(InfoQuery: boolean); override;
procedure CloseCursor; override;
procedure InternalOpen; override;
procedure InternalClose; override;
function IsCursorOpen: boolean; override;
procedure DataReopen; virtual;
procedure InternalRefresh; override;
procedure CheckFieldCompatibility(Field: TField; FieldDef: TFieldDef); override;
procedure DoAfterOpen; override;
{ Field Management }
procedure InternalInitFieldDefs; override;
procedure CreateFieldDefs; virtual;
procedure ClearCalcFields(Buffer: TRecordBuffer); override;
function GetObjectFieldDefName(Parent: TFieldDef; Index: integer; ObjType: TObjectType):string; virtual;
function GetFielDefSize(FieldType: TFieldType; FieldDesc: TFieldDesc): integer; virtual;
procedure GetObjectTypeNames(Fields: TFields);
function GetFieldType(DataType: word): TFieldType; overload; virtual;
function GetFieldType(FieldDesc: TFieldDesc): TFieldType; overload; virtual;
procedure SetFieldData(Field: TField; Buffer: TValueBuffer); override;
procedure SetFieldData(Field: TField; Buffer: TValueBuffer; NativeFormat: Boolean); override;
procedure DataConvert(Field: TField; Source, Dest: TValueBuffer; ToNative: Boolean); override; //TODO
function GetSparseArrays: boolean;
procedure SetSparseArrays(Value: boolean);
procedure SetNumberRange(FieldDef: TFieldDef); virtual;
{ Buffer/Record Management }
function AllocRecordBuffer: TRecordBuffer; override;
procedure FreeRecordBuffer(var Buffer: TRecordBuffer); override;
procedure CopyRecordBuffer(SrcBuffer: IntPtr; DstBuffer: IntPtr);
procedure InitRecord(Buffer: TRecordBuffer); override;
procedure InternalInitRecord(Buffer: TRecordBuffer); override;
function GetOldRecord: TRecordBuffer;
function GetActiveRecBuf(var RecBuf: TRecordBuffer): boolean;
function GetRecord(Buffer: TRecordBuffer; GetMode: TGetMode; DoCheck: boolean): TGetResult; override;
procedure BlockReadNext; override;
procedure SetBlockReadSize(Value: integer); override;
procedure FreeRefBuffers;
procedure FreeRefComplexFields(Buffer: TRecordBuffer; WithBlob: boolean = True);
{ Bookmarks }
procedure GetBookmarkData(Buffer: TRecordBuffer; {$IFDEF CLR}var{$ENDIF} Bookmark: TBookmark); override;
procedure SetBookmarkData(Buffer: TRecordBuffer; {$IFDEF CLR}const{$ENDIF} Bookmark: TBookmark); override;
function GetBookmarkFlag(Buffer: TRecordBuffer): TBookmarkFlag; override;
procedure SetBookmarkFlag(Buffer: TRecordBuffer; Value: TBookmarkFlag); override;
//function GetBookmarkStr: TBookmarkStr; override;
{ Navigation }
procedure InternalFirst; override;
procedure InternalLast; override;
procedure InternalGotoBookmark({$IFDEF CLR}const{$ENDIF} Bookmark: TBookmark); override;
procedure InternalSetToRecord(Buffer: TRecordBuffer); override;
{ Editing }
procedure InternalAddRecord(Buffer: IntPtr; Append: boolean); override;
procedure InternalInsert; override;
procedure InternalDelete; override;
procedure InternalEdit; override;
procedure InternalPost; override;
procedure InternalCancel; override;
procedure InternalDeferredPost; virtual;
function PerformAppend: boolean; virtual;
function PerformDelete: boolean; virtual;
function PerformUpdate: boolean; virtual;
procedure DoPerformAppend;
procedure DoPerformDelete;
procedure DoPerformUpdate;
procedure DoGetCachedFields;
procedure DoGetCachedBuffer(Buffer: IntPtr; Source: IntPtr = nil);
procedure SetDefaultExpressionValue(Field: TField); virtual;
procedure DoOnNewRecord; override;
{ Filter/Find/Locate }
procedure ActivateFilters;
procedure DeactivateFilters;
function RecordFilter(RecBuf: IntPtr): boolean;
procedure SetFilterData(const Text: string; Options:TFilterOptions);
procedure SetFiltered(Value: boolean); override;
procedure SetFilterOptions(Value: TFilterOptions); override;
procedure SetFilterText(const Value: string); override;
procedure SetOnFilterRecord(const Value: TFilterRecordEvent); override;
procedure CopyFieldValue(const Value: variant; out ValuePtr: IntPtr; out ValueType: integer; FieldDesc: TFieldDesc); virtual;
function LocateRecord(const KeyFields: string; const KeyValues: variant;
Options: TLocateExOptions; SavePos: boolean): boolean; overload;
function LocateRecord(const KeyFields: array of TField; const KeyValues: variant;
Options: TLocateExOptions; SavePos: boolean): boolean; overload;
function FindRecord(Restart, GoForward: boolean): boolean; override;
{ CachedUpdates }
function BatchUpdate: boolean; virtual;
function CanFlushBatch: boolean; virtual;
procedure FlushBatch; virtual;
procedure CheckCachedUpdateMode;
procedure DoApplyRecord(UpdateKind: TUpdateRecKind; var Action: TUpdateRecAction; LastItem: boolean);
{ Blobs }
//Renamed GetBlob (CBuilder5 bug - overloaded methods in different sections):
function InternalGetBlob(FieldDesc: TFieldDesc): TBlob;
function InternalSetBlob(FieldDesc: TFieldDesc; Blob: TBlob): boolean;
function SetBlob(Field: TField; Blob: TBlob): boolean;
procedure CloseBlob(Field: TField); override;
{ Misc }
function GetRecordCount: integer; override;
function GetRecordSize: word; override;
function GetRecNo: integer; override;
procedure SetRecNo(Value: integer); override;
procedure InternalHandleException; override;
procedure AssignTo(Dest: TPersistent); override;
{$IFDEF CLR}
procedure DataEvent(Event: TDataEvent; Info: TObject); override;
{$ELSE}
procedure DataEvent(Event: TDataEvent; Info: longint); override;
{$ENDIF}
function GetStateFieldValue(State: TDataSetState; Field: TField): Variant; override;
{ XML }
procedure WriteFieldXMLDataType(Field: TField; FieldDesc: TFieldDesc; const FieldAlias: string;
XMLWriter: XMLTextWriter); virtual;
procedure WriteFieldXMLAttributeType(Field: TField; FieldDesc: TFieldDesc; const FieldAlias: string;
XMLWriter: XMLTextWriter); virtual;
function GetFieldXMLValue(Field: TField; FieldDesc: TFieldDesc): string; virtual;
public
constructor Create(aOwner: TComponent); override;
destructor Destroy; override;
procedure Prepare; virtual;
procedure UnPrepare; virtual;
procedure CheckPrepared;
{ Fields }
function GetFieldDescNo(Field: TField): integer;
function GetFieldDesc(const Field: TField): TFieldDesc; overload; virtual;
function GetFieldData(Field: TField; Buffer: TValueBuffer): boolean; overload; override;
//function Translate(const Src: string; var Dest: string; ToOem: boolean): integer; override;
function GetFieldData(FieldNo: integer; Buffer: TValueBuffer): boolean; overload; override;
function GetFieldData(Field: TField; Buffer: TValueBuffer; NativeFormat: Boolean): Boolean; override;
function GetBlob(const FieldName: string): TBlob; overload;
function GetBlob(Field: TField): TBlob; overload;
{ Edit }
procedure Cancel; override;
procedure DeferredPost;
{ Bookmarks }
{$IFDEF CLR} // TDataSet bug
function GetBookmark: TBookmark; override;
procedure FreeBookmark(var Bookmark: TBookmark); override;
{$ENDIF}
function BookmarkValid({$IFDEF CLR}const{$ENDIF} Bookmark: TBookmark): boolean; override;
function CompareBookmarks({$IFDEF CLR}const{$ENDIF} Bookmark1, Bookmark2: TBookmark): integer; override;
function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; override;
function Locate(const KeyFields: string; const KeyValues: variant;
Options: TLocateOptions): boolean; overload; override;
function Locate(const KeyFields: array of TField; const KeyValues: variant;
Options: TLocateOptions): boolean; reintroduce; overload;
function LocateEx(const KeyFields: string; const KeyValues: variant;
Options: TLocateExOptions): boolean; overload;
function LocateEx(const KeyFields: array of TField; const KeyValues: variant;
Options: TLocateExOptions): boolean; overload;
function Lookup(const KeyFields: string; const KeyValues: variant;
const ResultFields: string): variant; override;
{ CachedUpdates }
function UpdateStatus: TUpdateStatus; override;
function UpdateResult: TUpdateAction;
procedure ApplyUpdates; virtual;
procedure CommitUpdates;
procedure CancelUpdates;
procedure RestoreUpdates;
procedure RevertRecord;
{ XML }
procedure SaveToXML(Destination: TStream); overload;
procedure SaveToXML(const FileName: string); overload;
function IsSequenced: boolean; override;
property Prepared: boolean read GetPrepared write SetPrepared;
property CachedUpdates: boolean read FCachedUpdates write SetCachedUpdates default False;
property UpdatesPending: boolean read GetUpdatesPending;
property LocalUpdate: boolean read FLocalUpdate write FLocalUpdate default False;
property UpdateRecordTypes: TUpdateRecordTypes read GetUpdateRecordSet write SetUpdateRecordSet;
property SparseArrays: boolean read GetSparseArrays write SetSparseArrays;
// obsolete
property LocalConstraints: boolean read FLocalConstraints write FLocalConstraints default True;
property OnUpdateError: TUpdateErrorEvent read FOnUpdateError write FOnUpdateError;
property OnUpdateRecord: TUpdateRecordEvent read FOnUpdateRecord write FOnUpdateRecord;
property IndexFieldNames: string read FIndexFieldNames write SetIndexFieldNames;
end;
{ TBlobStream }
TBlobStream = class(TStream)
protected
FField: TBlobField;
FDataSet: TMemDataSet;
FBuffer: TRecordBuffer;
FMode: TBlobStreamMode;
FFieldNo: integer;
FOpened: boolean;
FModified: boolean;
FPosition: longint;
function GetBlobSize: Longint;
protected
{$IFDEF CLR}
procedure SetSize(NewSize: Int64); override;
{$ELSE}
procedure SetSize(NewSize: Longint); override;
{$ENDIF}
public
constructor Create(Field: TBlobField; Mode: TBlobStreamMode);
destructor Destroy; override;
{$IFDEF CLR}
function Read(var Buffer: TBytes; Offset, Count: Longint): Longint; override;
function Write(const Buffer: TBytes; Offset, Count: Longint): Longint; override;
function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;
{$ELSE}
function Read(var Buffer; Count: longint): longint; override;
function Write(const Buffer; Count: Longint): Longint; override;
function Seek(Offset: Longint; Origin: Word): Longint; override;
{$ENDIF}
procedure Truncate;
end;
TMemDSUtils = class
public
class function SetBlob(Obj: TMemDataSet; Field: TField; Blob: TBlob): boolean;
class function GetBlob(Obj: TMemDataSet; FieldDesc: TFieldDesc): TBlob;
end;
function GetFieldType(DataType: word): TFieldType;
function GetDataType(FieldType: TFieldType): word;
function ChangeDecimalSeparator(const Value: string; OldSeparator, NewSeparator: string): string;
function XMLEncode(const AStr: String): String;
implementation
uses
{$IFDEF VER6P}
DateUtils,FmtBcd,
{$ENDIF}
DBConsts, DAConsts, Math{$IFDEF MSWINDOWS}, Windows{$ENDIF};
const
{$IFDEF VER4}
DataTypeMap: array [TFieldType] of word = (
// ftUnknown, ftString, ftSmallint, ftInteger, ftWord
dtUnknown, dtString, dtInt16, dtInteger, dtUInt16,
// ftBoolean, ftFloat, ftCurrency, ftBCD, ftDate, ftTime, ftDateTime,
dtBoolean, dtFloat, dtCurrency, dtBCD, dtDate, dtTime, dtDateTime,
// ftBytes, ftVarBytes, ftAutoInc, ftBlob, ftMemo, ftGraphic, ftFmtMemo,
dtBytes, dtVarBytes, dtInteger, dtBlob, dtMemo, 0, 0,
// ftParadoxOle, ftDBaseOle, ftTypedBinary, ftCursor, ftFixedChar, ftWideString,
0, 0, 0, 0, dtString, dtWideString,
// ftLargeint, ftADT, ftArray, ftReference, ftDataSet
dtInt64, dtObject, dtArray, dtReference, dtTable);
{$ENDIF}
{$IFDEF VER5}
DataTypeMap: array [TFieldType] of word = (
// ftUnknown, ftString, ftSmallint, ftInteger, ftWord
dtUnknown, dtString, dtInt16, dtInteger, dtUInt16,
// ftBoolean, ftFloat, ftCurrency, ftBCD, ftDate, ftTime, ftDateTime,
dtBoolean, dtFloat, dtCurrency, dtBCD, dtDate, dtTime, dtDateTime,
// ftBytes, ftVarBytes, ftAutoInc, ftBlob, ftMemo, ftGraphic, ftFmtMemo,
dtBytes, dtVarBytes, dtInteger, dtBlob, dtMemo, 0, 0,
// ftParadoxOle, ftDBaseOle, ftTypedBinary, ftCursor, ftFixedChar, ftWideString,
0, 0, 0, 0, dtString, dtWideString,
// ftLargeint, ftADT, ftArray, ftReference, ftDataSet, ftOraBlob, ftOraClob,
dtInt64, dtObject, dtArray, dtReference, dtTable, 0, 0,
// ftVariant, ftInterface, ftIDispatch, ftGuid
dtVariant, 0, 0, dtGuid);
{$ENDIF}
{$IFDEF VER6P}
{$IFNDEF VER10P}
DataTypeMap: array [TFieldType] of word = (
// ftUnknown, ftString, ftSmallint, ftInteger, ftWord
dtUnknown, dtString, dtInt16, dtInteger, dtUInt16,
// ftBoolean, ftFloat, ftCurrency, ftBCD, ftDate, ftTime, ftDateTime,
dtBoolean, dtFloat, dtCurrency, dtBCD, dtDate, dtTime, dtDateTime,
// ftBytes, ftVarBytes, ftAutoInc, ftBlob, ftMemo, ftGraphic, ftFmtMemo,
dtBytes, dtVarBytes, dtInteger, dtBlob, dtMemo, 0, 0,
// ftParadoxOle, ftDBaseOle, ftTypedBinary, ftCursor, ftFixedChar, ftWideString,
0, 0, 0, 0, dtString, dtWideString,
// ftLargeint, ftADT, ftArray, ftReference, ftDataSet, ftOraBlob, ftOraClob,
dtInt64, dtObject, dtArray, dtReference, dtTable, 0, 0,
// ftVariant, ftInterface, ftIDispatch, ftGuid, ftTimeStamp, ftFMTBcd
dtVariant, 0, 0, dtGuid, 0, dtFmtBCD);
{$ENDIF}
{$ENDIF}
{$IFDEF VER10P}
DataTypeMap: array [TFieldType] of word = (
// ftUnknown, ftString, ftSmallint, ftInteger, ftWord
dtUnknown, dtString, dtInt16, dtInteger, dtUInt16,
// ftBoolean, ftFloat, ftCurrency, ftBCD, ftDate, ftTime, ftDateTime,
dtBoolean, dtFloat, dtCurrency, dtBCD, dtDate, dtTime, dtDateTime,
// ftBytes, ftVarBytes, ftAutoInc, ftBlob, ftMemo, ftGraphic, ftFmtMemo,
dtBytes, dtVarBytes, dtInteger, dtBlob, dtMemo, 0, 0,
// ftParadoxOle, ftDBaseOle, ftTypedBinary, ftCursor, ftFixedChar, ftWideString,
0, 0, 0, 0, dtString, dtWideString,
// ftLargeint, ftADT, ftArray, ftReference, ftDataSet, ftOraBlob, ftOraClob,
dtInt64, dtObject, dtArray, dtReference, dtTable, 0, 0,
// ftVariant, ftInterface, ftIDispatch, ftGuid, ftTimeStamp, ftFMTBcd, ftFixedWideChar
dtVariant, 0, 0, dtGuid, 0, dtFmtBCD, dtWideString,
// ftWideMemo, ftOraTimeStamp, ftOraInterval
dtWideMemo, 0, 0);
{$ENDIF}
function GetFieldType(DataType: word): TFieldType;
begin
case DataType of
dtUnknown:
Result := ftUnknown;
dtString:
Result := ftString;
dtWideString:
Result := ftWideString;
dtInt8:
Result := ftSmallint;
dtInt16:
Result := ftSmallint;
dtWord:
Result := ftWord;
dtInteger:
Result := ftInteger;
dtUInt32:
Result := ftLargeInt;
dtLargeint:
Result := ftLargeInt;
dtFloat:
Result := ftFloat;
dtDate:
Result := ftDate;
dtTime:
Result := ftTime;
dtDateTime:
Result := ftDateTime;
dtMemo:
Result := ftMemo;
dtWideMemo:
Result := {$IFDEF VER10P}ftWideMemo;{$ELSE}ftMemo;{$ENDIF}
dtBlob:
Result := ftBlob;
dtObject:
Result := ftADT;
dtReference:
Result := ftReference;
dtArray:
Result := ftArray;
dtTable:
Result := ftDataSet;
dtBoolean:
Result := ftBoolean;
{$IFDEF VER5P}
dtVariant:
Result := ftVariant;
{$ENDIF}
dtExtString:
Result := ftString;
dtExtWideString:
Result := ftWideString;
dtBytes:
Result := ftBytes;
dtVarBytes:
Result := ftVarBytes;
dtExtVarBytes:
Result := ftVarBytes;
dtBCD:
Result := ftBCD;
{$IFDEF VER6P}
dtFmtBCD:
Result := ftFMTBcd;
{$ENDIF}
dtGuid:
Result := ftGuid;
dtCurrency:
Result := ftCurrency;
else
Assert(False, SUnknownDataType);
Result := ftUnknown;
end;
end;
function GetDataType(FieldType: TFieldType): word;
begin
Result := DataTypeMap[FieldType];
end;
{$IFDEF CLR}
{ PRecInfo }
function PRecInfo.GetRecordNumber: longint;
begin
Result := Marshal.ReadInt32(Ptr);
end;
procedure PRecInfo.SetRecordNumber(const Value: longint);
begin
Marshal.WriteInt32(Ptr, Value);
end;
function PRecInfo.GetUpdateStatus: TUpdateStatus;
begin
Result := TUpdateStatus(Marshal.ReadByte(Ptr, sizeof(longint)));
end;
procedure PRecInfo.SetUpdateStatus(const Value: TUpdateStatus);
begin
Marshal.WriteByte(Ptr, sizeof(longint), byte(Value));
end;
function PRecInfo.GetBookmarkFlag: TBookmarkFlag;
begin
Result := TBookmarkFlag(Marshal.ReadByte(Ptr, sizeof(integer) + sizeof(TUpdateStatus)));
end;
procedure PRecInfo.SetBookmarkFlag(const Value: TBookmarkFlag);
begin
Marshal.WriteByte(Ptr, sizeof(integer) + sizeof(TUpdateStatus), byte(Value));
end;
function PRecInfo.GetRefComplexFields: boolean;
begin
Result := boolean(Marshal.ReadByte(Ptr, sizeof(integer) + sizeof(TUpdateStatus)
+ sizeof(TBookmarkFlag)));
end;
procedure PRecInfo.SetRefComplexFields(const Value: boolean);
begin
Marshal.WriteByte(Ptr, sizeof(integer) + sizeof(TUpdateStatus)
+ sizeof(TBookmarkFlag), byte(Value));
end;
class operator PRecInfo.Implicit(AValue: IntPtr): PRecInfo;
begin
Result.Ptr := AValue;
end;
class operator PRecInfo.Implicit(AValue: integer): PRecInfo;
begin
Result.Ptr := IntPtr(AValue);
end;
{$ENDIF}
{ TMemDataSet }
constructor TMemDataSet.Create(aOwner:TComponent);
begin
inherited Create(aOwner);
FLocalConstraints := True;
FCreateCalcFieldDescs := True;
CreateIRecordSet;
end;
destructor TMemDataSet.Destroy;
begin
inherited;
UnPrepare;
FreeIRecordSet;
end;
procedure TMemDataSet.CreateIRecordSet;
begin
SetIRecordSet(TMemData.Create);
end;
procedure TMemDataSet.FreeIRecordSet;
begin
Data.Free;
end;
procedure TMemDataSet.SetIRecordSet(Value: TData);
begin
Data := Value;
if Data <> nil then begin
Data.CachedUpdates := FCachedUpdates;
Data.OnAppend := DoPerformAppend;
Data.OnDelete := DoPerformDelete;
Data.OnUpdate := DoPerformUpdate;
Data.OnApplyRecord := DoApplyRecord;
if FCreateCalcFieldDescs then begin
TMemData(Data).OnGetCachedFields := DoGetCachedFields;
TMemData(Data).OnGetCachedBuffer := DoGetCachedBuffer;
end;
end;
end;
{ Open/Close DataSet }
procedure TMemDataSet.Prepare;
begin
if not Prepared then begin
Data.Prepare;
CreateFieldDefs;
end;
end;
procedure TMemDataSet.UnPrepare;
begin
if Active then
Close;
Data.UnPrepare;
if not (csDestroying in ComponentState) then // This line may be called after destroing FieldDefs. For details see TMemDataSet.Destroy
FieldDefs.Updated := False;
end;
procedure TMemDataSet.CheckPrepared;
begin
if not Prepared then
DatabaseError(SDataSetIsNotPrepared);
end;
procedure TMemDataSet.InternalOpen;
var
Field: TField;
i: integer;
begin
Data.Open;
CreateFieldDefs;
// Update FieldDefs once to avoid multiple Update calls when working with FieldDefsList
// (Perfomance optimization)
// FieldDefs.Updated := False;
// FieldDefs.Update;
if DefaultFields then
CreateFields
else // Setting actual size
for i := 0 to FieldDefs.Count - 1 do
if FieldDefs[i].DataType = ftString then begin
Field := FindField(FieldDefs[i].Name);
if (Field <> nil) and (Field.FieldKind = fkData) then begin
CheckFieldCompatibility(Field, FieldDefs[i]);
Field.Size := FieldDefs[i].Size;
end;
end;
// Set number specific
if FNumberRange then
for i := 0 to FieldDefs.Count - 1 do
SetNumberRange(FieldDefs[i]);
BindFields(True);
if (Data.Fields.Count > 0) and (Data.Fields[Data.Fields.Count - 1].FieldDescKind = fdkCached) then
TMemData(Data).UpdateCachedBuffer(nil, nil);
if ObjectView then
GetObjectTypeNames(Fields);
if (Data is TMemData) and (TMemData(Data).IndexFields.Count > 0) then
TMemData(Data).SortItems;
BookmarkSize := SizeOf(TRecBookmark);
//FBlobCacheOfs := Data.RecordSize + CalcFieldsSize;
if not FCreateCalcFieldDescs then
FRecInfoOfs := Data.RecordSize + CalcFieldsSize
else
if FCacheCalcFields then
FRecInfoOfs := Data.RecordSize
else
FRecInfoOfs := Data.RecordSize + Data.CalcRecordSize; //FBlobCacheOfs + BlobFieldCount * SizeOf(Pointer);
FBookmarkOfs := FRecInfoOfs + SizeOf(TRecInfo);
FRecBufSize := FBookmarkOfs + BookmarkSize;
{$IFNDEF CLR}
{$IFNDEF VER10P}
if not FCreateCalcFieldDescs then begin
FWideStringOfs := FRecBufSize;
if CalcFieldsSize > 0 then
for i := 0 to Fields.Count - 1 do
if (Fields[i].DataType = ftWideString) and (Fields[i].FieldKind in [fkCalculated, fkLookUp]) then
FRecBufSize := FRecBufSize + (Fields[i].Size + 1) * sizeof(WideChar);
end;
{$ENDIF}
{$ENDIF}
FInCacheProcessing := False;
if Filtered then begin
ActivateFilters;
Data.FilterUpdated;
end;
end;
function TMemDataSet.IsCursorOpen: boolean;
begin
Result := Data.Active;
end;
procedure TMemDataSet.InternalClose;
begin
BindFields(False);
if DefaultFields then
DestroyFields;
Data.Close;
end;
procedure TMemDataSet.OpenCursor(InfoQuery: boolean);
begin
inherited;
if not InfoQuery then begin
if FOldRecBuf <> nil then begin
FreeRecordBuffer(FOldRecBuf);
FOldRecBuf := nil;
end;
FOldRecBuf := AllocRecordBuffer;
end;
//DataEvent(deDataSetChange, 0); // Notify nested datasets // DEBUG
end;
procedure TMemDataSet.CloseCursor;
var
Buffer: IntPtr;
RecInfo: PRecInfo;
begin
// free complex fields if call Close in dsInsert or dsEdit mode
// TDataSet.Close doesn't call Cancel
if Data.HasComplexFields then begin
if FInInserting then begin
Buffer := ActiveBuffer;
RecInfo := PRecInfo(integer(Buffer) + FRecInfoOfs);
if RecInfo.RefComplexFields then begin
Data.FreeComplexFields(Buffer, True);
RecInfo.RefComplexFields := False;
end;
end;
if FInEditing then begin
Buffer := ActiveBuffer;
RecInfo := PRecInfo(integer(Buffer) + FRecInfoOfs);
if RecInfo.RefComplexFields then begin
Data.FreeComplexFields(Buffer, False); // Blobs isn't created
RecInfo.RefComplexFields := False;
end;
end;
if FInInserting then
Data.FreeComplexFields(ActiveBuffer, True);
if FInEditing then
Data.FreeComplexFields(ActiveBuffer, False); // Blobs isn't created
FInInserting := False;
FInEditing := False;
end;
if FOldRecBuf <> nil then begin
FreeRecordBuffer(FOldRecBuf);
FOldRecBuf := nil;
end;
try
inherited;
finally
FParentDataSet := nil;
end;
end;
procedure TMemDataSet.DataReopen;
begin
Data.Reopen;
end;
procedure TMemDataSet.InternalRefresh;
begin
FreeRefBuffers;
//ClearBuffers; /// CR11512
DataReopen;
if (Data is TMemData) and (TMemData(Data).IndexFields.Count > 0) then
TMemData(Data).SortItems;
end;
procedure TMemDataSet.CheckFieldCompatibility(Field: TField; FieldDef: TFieldDef);
begin
with Field do
begin
{$IFDEF VER6P}
if (DataType <= ftFMTBcd) and (FieldDef.DataType <= ftFMTBcd) then
{$ELSE}
if (DataType <= ftGuid) and (FieldDef.DataType <= ftGuid) then
{$ENDIF}
inherited CheckFieldCompatibility(Field, FieldDef)
else
if DataType <> FieldDef.DataType then
DatabaseErrorFmt(SFieldTypeMismatch, [DisplayName,
FieldTypeNames[DataType], FieldTypeNames[FieldDef.DataType]], Self);
end;
end;
procedure TMemDataSet.DoAfterOpen;
var
i: integer;
begin
inherited;
for i := 0 to NestedDataSets.Count - 1 do
with TDataSet(NestedDataSets[i]) do
if Active then
DataEvent(deParentScroll, {$IFDEF CLR}nil{$ELSE}0{$ENDIF});
end;
procedure TMemDataSet.FreeRefBuffers;
var
i: integer;
begin
if FNeedAddRef then
for i := 0 to BufferCount do
FreeRefComplexFields(Buffers[i]);
FreeRefComplexFields(TempBuffer);
end;
procedure TMemDataSet.FreeRefComplexFields(Buffer: TRecordBuffer; WithBlob: boolean);
var
RecInfo: PRecInfo;
begin
RecInfo := PRecInfo(integer(Buffer) + FRecInfoOfs);
if RecInfo.RefComplexFields then begin
Data.FreeComplexFields(Buffer, WithBlob);
RecInfo.RefComplexFields := False;
end;
end;
{ Field Management }
procedure TMemDataSet.InternalInitFieldDefs;
var
CheckDefs: boolean;
i: integer;
OldFieldNames: array of string;
OldFieldCount: integer;
begin
// can't CreateFieldDefs if FieldDefs.Update(InitFieldDefs)
if not Data.Active then begin
OldFieldCount := Data.Fields.Count;
CheckDefs := (FieldDefs <> nil) and not FieldDefs.Updated;
if CheckDefs then begin
SetLength(OldFieldNames, OldFieldCount);
for i := 0 to OldFieldCount - 1 do
OldFieldNames[i] := Data.Fields[i].Name;
end;
Data.InitFields;
if OldFieldCount <> Data.Fields.Count then
FieldDefs.Updated := False
else
if CheckDefs then
for i := 0 to OldFieldCount - 1 do
if OldFieldNames[i] <> Data.Fields[i].Name then begin
FieldDefs.Updated := False;
Break;
end;
CreateFieldDefs;
end;
end;
function TMemDataSet.GetFieldType(DataType: word): TFieldType;
begin
Result := {$IFDEF CLR}CoreLab.Dac.{$ENDIF}MemDS.GetFieldType(DataType);
end;
function TMemDataSet.GetFieldType(FieldDesc: TFieldDesc): TFieldType;
begin
Result := GetFieldType(FieldDesc.DataType);
end;
function TMemDataSet.GetObjectFieldDefName(Parent: TFieldDef; Index: integer; ObjType: TObjectType):string;
begin
Result := IntToStr(Index);
end;
function TMemDataSet.GetFielDefSize(FieldType: TFieldType; FieldDesc: TFieldDesc): integer;
begin
Result := 0;
case FieldType of
ftString, ftWideString{$IFDEF VER5P},ftGuid{$ENDIF}: begin
Result := FieldDesc.Length;
if Result = 0 then
Result := 1; // For SELECT NULL FROM ...
end;
ftBytes, ftVarBytes:
Result := FieldDesc.Length;
end;
end;
procedure TMemDataSet.CreateFieldDefs;
function GetFieldName(ParentFieldName, FieldName: string; Index: integer): string;
var
NewFieldName: string;
begin
if Index = 0 then
NewFieldName := FieldName
else
NewFieldName := FieldName + '_' + IntToStr(Index);
if (FieldDefs.IndexOf(ParentFieldName + '.' + NewFieldName) <> -1) then
Result := GetFieldName(ParentFieldName, FieldName, Index + 1)
else
Result := NewFieldName;
end;
procedure CreateObjectFields(ObjType: TObjectType; Parent: TFieldDef);
var
i: integer;
FieldDef: TFieldDef;
FieldType: TFieldType;
aSize: word;
Item,CountItem: integer;
FieldName: string;
begin
if (ObjType.DataType = dtObject) or SparseArrays then
CountItem := 1
else begin
CountItem := ObjType.Size;
if CountItem > MaxArrayItem then // Restriction of array length
CountItem := MaxArrayItem;
end;
for i := 0 to ObjType.AttributeCount - 1 do begin
for Item := 0 to CountItem - 1 do begin
with ObjType.Attributes[i] do begin
FieldType := GetFieldType(DataType);
aSize := 0;
case FieldType of
ftString:
aSize := Length;
end;
FieldDef := TFieldDef.Create(Parent.ChildDefs);
if ObjType.DataType = dtObject then
FieldName := Name
else
FieldName := GetObjectFieldDefName(Parent, Item, ObjType);
FieldDef.Name := GetFieldName(Parent.Name, FieldName, 0);
FieldDef.DataType := FieldType;
FieldDef.Size := aSize;
FieldDef.Required := False;
if DB.faReadonly in Parent.Attributes then
FieldDef.Attributes := FieldDef.Attributes + [DB.faReadonly];
if FieldType in [ftADT,ftArray] then
CreateObjectFields(ObjectType, FieldDef);
end;
end;
end;
end;
var
FieldType: TFieldType;
aSize: word;
i: integer;
FieldDef: TFieldDef;
begin
if FieldDefs.Updated then Exit;
FieldDefs.BeginUpdate;
try
FieldDefs.Clear;
for i := 0 to Data.FieldCount - 1 do
with Data.Fields[i] do
if not HiddenObject and (FieldDescKind = fdkData) and (not HasParent or ParentField.HiddenObject) then begin
// FieldNo 1..
FieldType := GetFieldType(Data.Fields[i]);
aSize := GetFielDefSize(FieldType, Data.Fields[i]);
if FieldType <> ftUnknown then begin
FieldDef := TFieldDef.Create(FieldDefs, Name, FieldType, aSize,
Required and FLocalConstraints, i + 1);
if ReadOnly then
FieldDef.Attributes := FieldDef.Attributes + [DB.faReadonly];
if FieldType in [ftFloat, ftInteger] then
FieldDef.Precision := Length
else
if FieldType in [ftBCD{$IFDEF VER6P}, ftFMTBCD{$ENDIF}] then begin
FieldDef.Precision := Length;
FieldDef.Size := Scale;
end
else
if FieldType in [ftADT, ftArray] then
CreateObjectFields(ObjectType, FieldDef);
if Hidden then
FieldDef.Attributes := FieldDef.Attributes + [DB.faHiddenCol];
{$IFDEF VER5P}
if Fixed then
FieldDef.Attributes := FieldDef.Attributes + [DB.faFixed];
{$ENDIF}
end;
end;
finally
FieldDefs.EndUpdate;
end;
FieldDefList.Update;
FieldDefs.Updated := True;
end;
procedure TMemDataSet.GetObjectTypeNames(Fields: TFields);
var
i: integer;
ObjectField: TObjectField;
begin
for i := 0 to Fields.Count - 1 do
if Fields[i] is TObjectField then begin
ObjectField := TObjectField(Fields[i]);
ObjectField.ObjectType := Data.Fields[Fields[i].FieldNo - 1].ObjectType.Name;
with ObjectField do
if DataType in [ftADT, ftArray] then begin
if (DataType = ftArray) and SparseArrays and
(Fields[0].DataType = ftADT)
then
GetObjectTypeNames(TObjectField(Fields[0]).Fields)
else
GetObjectTypeNames(Fields);
end;
end
end;
function TMemDataSet.GetFieldDescNo(Field: TField): integer;
var
FieldDesc: TFieldDesc;
begin
FieldDesc := GetFieldDesc(Field);
if FieldDesc <> nil then
Result := FieldDesc.FieldNo
else
raise Exception.Create(Format(SFieldNotFound, [Field.FieldName]));
end;
function TMemDataSet.GetFieldDesc(const Field: TField): TFieldDesc;
var
FieldDesc: TFieldDesc;
i: integer;
Found: boolean;
begin
Assert(Data <> nil, 'FIRecordSet must be setted to this time');
Assert(Field <> nil, 'Field cannot be nil');
Assert((Field.DataSet = Self) or (Field.DataSet = nil {CR 22356}), 'Wrong DataSet');
{if Field.DataSet <> Self then
for i := 0 to Data.FieldCount - 1 do
if AnsiLowerCase(Field.FieldName) = AnsiLowerCase(Data.Fields[i].Name) then begin /// Field.FieldName must be equal with FieldDesc.Name
Result := Data.Fields[i];
Exit;
end;}
Result := nil;
if Field.FieldNo > 0 then
Result := TFieldDesc(Data.Fields[Field.FieldNo - 1])
else
begin
if DefaultFields and (Fields.Count = Data.Fields.Count {just in case; should be moved to Assert}) then begin
i := Fields.IndexOf(Field);
Result := Data.Fields[i];
end
else
for i := 0 to Data.FieldCount - 1 do begin
FieldDesc := Data.Fields[i];
Found := AnsiLowerCase(Field.FieldName) = AnsiLowerCase(FieldDesc.Name); /// Field.FieldName must be equal with FieldDesc.Name
if (Field.FieldKind <> fkData) then begin
if FieldDesc.FieldDescKind = fdkData then
Found := False
else
if not Found then
Found := AnsiLowerCase(Field.FieldName) = AnsiLowerCase(FieldDesc.ActualName);
end;
if Found then begin
Result := FieldDesc;
Break;
end;
end;
end;
// Assert(AnsiLowerCase(Field.FieldName) = AnsiLowerCase(Result.Name), Format('Field.FieldName <> FieldDesc.Name' + LineSeparator + '"%s" <> "%s"' + LineSeparator, [Field.FieldName, Result.Name]));
end;
function TMemDataSet.GetFieldData(Field: TField; Buffer: TValueBuffer): boolean;
var
IsBlank: boolean;
RecBuf: TRecordBuffer;
FieldBuf: IntPtr;
{$IFNDEF CLR}
{$IFNDEF VER10P}
DataOffset: LongInt;
{$ENDIF}
{$ENDIF}
i: integer;
FieldDesc: TFieldDesc;
begin
Result := False;
if not GetActiveRecBuf(RecBuf) then
Exit;
with Field do
if FieldNo > 0 then begin
Data.GetField(FieldNo, RecBuf, Buffer, IsBlank);
Result := not IsBlank;
end
else
if State in [dsBrowse, dsEdit, dsInsert, dsCalcFields, dsFilter, dsBlockRead] then
if FCreateCalcFieldDescs then begin
FieldDesc := nil;
for i := 0 to Length(FCalcFieldsMapping) - 1 do
if FCalcFieldsMapping[i].Field = Field then begin
FieldDesc := FCalcFieldsMapping[i].FieldDesc;
break;
end;
if FieldDesc <> nil then
Data.GetField(FieldDesc.FieldNo, RecBuf, Buffer, IsBlank)
else
Data.GetField(GetFieldDescNo(Field), RecBuf, Buffer, IsBlank);
Result := not IsBlank;
end
else begin
FieldBuf := IntPtr(integer(RecBuf) + RecordSize + Offset);
Result := Boolean(Marshal.ReadByte(FieldBuf));
if Result and (Buffer <> nil) then
{$IFNDEF CLR}
{$IFNDEF VER10P}
if Field.DataType = ftWideString then begin
DataOffset := Integer(IntPtr(Integer(FieldBuf) + 1)^);
CopyBuffer(IntPtr(Integer(RecBuf) + FWideStringOfs + DataOffset), Buffer, (Size + 1) * SizeOf(WideChar));
end
else
{$ENDIF}
{$ENDIF}
CopyBuffer(IntPtr(integer(FieldBuf) + 1), Buffer, DataSize);
end;
end;
function TMemDataSet.GetFieldData(FieldNo: integer; Buffer: TValueBuffer): boolean;
var
IsBlank: boolean;
RecBuf: TRecordBuffer;
begin
//if BlockReadSize > 0 then
Result := GetActiveRecBuf(RecBuf);
if Result then begin
Data.GetField(FieldNo, RecBuf, Buffer, IsBlank);
Result := not IsBlank;
end
end;
procedure TMemDataSet.SetFieldData(Field: TField; Buffer: TValueBuffer);
var
RecBuf: TRecordBuffer;
FieldBuf: IntPtr;
{$IFNDEF CLR}
{$IFNDEF VER10P}
DataOffset, WideStringSize: integer;
{$ENDIF}
{$ENDIF}
FieldDesc: TFieldDesc;
i: integer;
begin
with Field do begin
if not (State in dsWriteModes) then
DatabaseError(SNotEditing);
GetActiveRecBuf(RecBuf);
if FieldNo > 0 then begin
if State = dsCalcFields then
DatabaseError(SNotEditing);
if ReadOnly and not (State in [dsSetKey, dsFilter]) then
DatabaseErrorFmt(SFieldReadOnly, [DisplayName]);
Validate(Buffer);
if FieldKind <> fkInternalCalc then begin
Data.PutField(FieldNo, RecBuf, Buffer);
end;
end
else
if FCreateCalcFieldDescs then begin
FieldDesc := nil;
for i := 0 to Length(FCalcFieldsMapping) - 1 do
if FCalcFieldsMapping[i].Field = Field then begin
FieldDesc := FCalcFieldsMapping[i].FieldDesc;
break;
end;
if FieldDesc <> nil then
Data.PutField(FieldDesc.FieldNo, RecBuf, Buffer)
else
Data.PutField(GetFieldDescNo(Field), RecBuf, Buffer);
end
else begin
FieldBuf := IntPtr(integer(RecBuf) + RecordSize + Offset);
Marshal.WriteByte(FieldBuf, Integer(Integer(Buffer) <> 0) );
if Integer(Buffer) <> 0 then
{$IFNDEF CLR}
{$IFNDEF VER10P}
if Field.DataType = ftWideString then begin
WideStringSize := Length(WideString(Buffer^)) * Sizeof(WideChar);
DataOffset := Integer(IntPtr(integer(FieldBuf) + 1)^);
CopyBuffer(Pointer(Buffer^), IntPtr(Integer(RecBuf) + FWideStringOfs + DataOffset), WideStringSize);
Marshal.WriteInt16(IntPtr(Integer(RecBuf) + FWideStringOfs + DataOffset + WideStringSize), 0);
end
else
{$ENDIF}
{$ENDIF}
CopyBuffer(Buffer, IntPtr(integer(FieldBuf) + 1), DataSize);
end;
if not (State in [dsCalcFields, dsFilter, dsNewValue]) then
{$IFDEF CLR}
DataEvent(deFieldChange, Field);
{$ELSE}
DataEvent(deFieldChange, longint(Field));
{$ENDIF}
end;
end;
function TMemDataSet.GetFieldData(Field: TField; Buffer: TValueBuffer;
NativeFormat: Boolean): Boolean;
{$IFNDEF CLR}
{$IFNDEF VER10P}
var
Temp: PWideChar;
{$ENDIF}
{$ENDIF}
begin
if (Field.DataType = ftWideString) and (Buffer <> nil) then begin
{$IFDEF CLR}
Result := inherited GetFieldData(Field, Buffer, True);
{$ELSE}
{$IFDEF VER10P}
Result := inherited GetFieldData(Field, Buffer, True);
{$ELSE}
{ Cannot copy direct - may be conflict with Delphi string manager
SetLength(WideString(Buffer^), Field.Size * sizeof(WideChar));
Result := inherited GetFieldData(Field, PWideChar(WideString(Buffer^)), True);}
GetMem(Temp, (Field.Size + 1 {#0} + 8) * sizeof(WideChar));//+ 8 for numbers
try
Result := inherited GetFieldData(Field, Temp, True);
if Result then
WideString(Buffer^) := Temp
else
WideString(Buffer^) := '';
finally
FreeMem(Temp);
end;
{$ENDIF}
{$ENDIF}
end
else
Result := inherited GetFieldData(Field, Buffer, NativeFormat);
end;
procedure TMemDataSet.SetFieldData(Field: TField; Buffer: TValueBuffer; NativeFormat: Boolean);
begin
if Field is TWideStringField then begin
//Assert(Buffer <> nil);
inherited SetFieldData(Field, Buffer, True);
end
else
inherited SetFieldData(Field, Buffer, NativeFormat);
end;
function TMemDataSet.GetStateFieldValue(State: TDataSetState; Field: TField): Variant;
begin
if ((Self.State = dsInsert) and (State = dsOldValue)) or (State = dsInsert) then
Result := NULL
else
Result := inherited GetStateFieldValue(State, Field);
end;
procedure TMemDataSet.DataConvert(Field: TField; Source, Dest: TValueBuffer; ToNative: Boolean);
{$IFDEF CLR}
var
TimeStamp: TTimeStamp;
TempDouble: Double;
{$ENDIF}
begin
case Field.DataType of
{$IFDEF CLR}
ftDate:
if ToNative then
begin
TimeStamp := DateTimeToTimeStamp(
BitConverter.Int64BitsToDouble(Marshal.ReadInt64(Source)));
Marshal.WriteInt32(Dest, TimeStamp.Date);
end
else
begin
TimeStamp.Time := 0;
TimeStamp.Date := Marshal.ReadInt32(Source);
Marshal.WriteInt64(Dest,
BitConverter.DoubleToInt64Bits(MemUtils.TimeStampToDateTime(TimeStamp)));
end;
ftTime:
if ToNative then
begin
TimeStamp := DateTimeToTimeStamp(
BitConverter.Int64BitsToDouble(Marshal.ReadInt64(Source)));
Marshal.WriteInt32(Dest, TimeStamp.Time);
end
else
begin
TimeStamp.Time := Marshal.ReadInt32(Source);
TimeStamp.Date := DateDelta;
Marshal.WriteInt64(Dest,
BitConverter.DoubleToInt64Bits(MemUtils.TimeStampToDateTime(TimeStamp)));
end;
ftDateTime:
if ToNative then
begin
TempDouble := TimeStampToMSecs(DateTimeToTimeStamp(
BitConverter.Int64BitsToDouble(Marshal.ReadInt64(Source))));
Marshal.WriteInt64(Dest, BitConverter.DoubleToInt64Bits(TempDouble));
end
else
begin
TimeStamp := MSecsToTimeStamp(
Trunc(BitConverter.Int64BitsToDouble(Marshal.ReadInt64(Source))));
Marshal.WriteInt64(Dest,
BitConverter.DoubleToInt64Bits(MemUtils.TimeStampToDateTime(TimeStamp)));
end;
{$ENDIF}
ftBCD:
Marshal.WriteInt64(Dest, Marshal.ReadInt64(Source));
else
inherited DataConvert(Field, Source, Dest, ToNative);
end;
end;
function TMemDataSet.GetSparseArrays: boolean;
begin
Result := inherited SparseArrays;
end;
procedure TMemDataSet.SetSparseArrays(Value: boolean);
begin
if SparseArrays <> Value then begin
UnPrepare;
inherited SparseArrays := Value;
if Data <> nil then
Data.SparseArrays := Value;
end;
end;
function TMemDataSet.InternalGetBlob(FieldDesc: TFieldDesc): TBlob;
var
RecBuf: TRecordBuffer;
Ptr: IntPtr;
IsBlank: boolean;
begin
Ptr := Marshal.AllocHGlobal(sizeof(IntPtr));
try
if GetActiveRecBuf(RecBuf) then begin
if not Data.IsBlobFieldType(FieldDesc.DataType) then
DatabaseError(SNeedBlobType);
Data.GetField(FieldDesc.FieldNo, RecBuf, Ptr, IsBlank);
Result := TBlob(GetGCHandleTarget(Marshal.ReadIntPtr(Ptr)));
end
else
Result := nil;
finally
Marshal.FreeHGlobal(Ptr);
end;
end;
function TMemDataSet.GetBlob(const FieldName: string): TBlob;
var
FieldDesc: TFieldDesc;
begin
FieldDesc := Data.FieldByName(FieldName);
Result := InternalGetBlob(FieldDesc);
end;
function TMemDataSet.GetBlob(Field: TField): TBlob;
var
FieldDesc: TFieldDesc;
begin
if Field <> nil then begin
FieldDesc := GetFieldDesc(Field);
if FieldDesc = nil then
raise Exception.Create(Format(SFieldNotFound, [Field.FieldName]));
Result := InternalGetBlob(FieldDesc);
end
else
Result := nil;
end;
function TMemDataSet.InternalSetBlob(FieldDesc: TFieldDesc; Blob: TBlob): boolean;
var
OldBlob: TBlob;
RecBuf: TRecordBuffer;
begin
Assert(FieldDesc <> nil);
Assert(Blob <> nil);
OldBlob := InternalGetBlob(FieldDesc);
Assert(OldBlob <> nil);
Result := Blob.ClassType = OldBlob.ClassType;
if GetActiveRecBuf(RecBuf) then begin
if not Data.IsBlobFieldType(FieldDesc.DataType) then
DatabaseError(SNeedBlobType);
Assert((Blob.RefCount = 1) and (OldBlob.RefCount = 1));
OldBlob.Free;
Blob.AddRef;
Marshal.WriteIntPtr(RecBuf, FieldDesc.Offset, Blob.GCHandle);
end
else
Result := False;
end;
function TMemDataSet.SetBlob(Field: TField; Blob: TBlob): boolean;
var
FieldDesc: TFieldDesc;
begin
if Field <> nil then begin
FieldDesc := GetFieldDesc(Field);
if FieldDesc = nil then
raise Exception.Create(Format(SFieldNotFound, [Field.FieldName]));
Result := InternalSetBlob(FieldDesc, Blob);
end
else
Result := False;
end;
procedure TMemDataSet.SetNumberRange(FieldDef: TFieldDef);
var
Field: TField;
FieldDesc: TFieldDesc;
begin
if FieldDef.DataType in [ftInteger, ftFloat] then begin
Field := FindField(FieldDef.Name);
if Field <> nil then begin
CheckFieldCompatibility(Field, FieldDef);
FieldDesc := Data.FindField(FieldDef.Name);
if FieldDef.DataType = ftInteger then begin
Assert(Field is TIntegerField);
TIntegerField(Field).MaxValue := Round(IntPower(10, FieldDesc.Length)) - 1;
TIntegerField(Field).MinValue := -TIntegerField(Field).MaxValue;
end
else
if (FieldDesc.Length > 0) and (FieldDesc.Length <= 15) then begin
Assert(Field is TFloatField);
TFloatField(Field).Precision := FieldDesc.Length;
TFloatField(Field).MaxValue :=
IntPower(10, FieldDesc.Length - FieldDesc.Scale) -
IntPower(10, - FieldDesc.Scale);
TFloatField(Field).MinValue := - TFloatField(Field).MaxValue;
end;
end;
end;
end;
{function TMemDataSet.Translate(const Src: string; var Dest: string; boolean): integer;
begin
inherited Translate(Src, Dest, ToOem);
Result := StrLen(Src);
{ if ToOem then
AnsiToNativeBuf(Locale, Src, Dest, Result)
else
NativeToAnsiBuf(Locale, Src, Dest, Result);
if Src <> Dest then
Dest[Result] := #0;
end;}
{ Buffer/Record Management }
function TMemDataSet.AllocRecordBuffer: TRecordBuffer;
begin
Result := Marshal.AllocHGlobal(FRecBufSize);
PRecInfo(integer(Result) + FRecInfoOfs).RefComplexFields := False;
end;
procedure TMemDataSet.FreeRecordBuffer(var Buffer: TRecordBuffer);
begin
FreeRefComplexFields(Buffer);
Marshal.FreeHGlobal(Buffer);
Buffer := nil;
end;
procedure TMemDataSet.CopyRecordBuffer(SrcBuffer: IntPtr; DstBuffer: IntPtr);
begin
Data.FreeComplexFields(DstBuffer, False);
CopyBuffer(SrcBuffer, DstBuffer, FRecBufSize);
PRecInfo(integer(DstBuffer) + FRecInfoOfs).RefComplexFields := True;
Data.CreateComplexFields(DstBuffer, False); //copy complex fields
Data.CopyComplexFields(SrcBuffer, DstBuffer, False);
end;
function TMemDataSet.GetRecord(Buffer: TRecordBuffer; GetMode: TGetMode;
DoCheck: Boolean): TGetResult;
var
{$IFDEF CLR}
RecInfo: IntPtr;
{$ELSE}
RecInfo: PRecInfo;
{$ENDIF}
begin
FreeRefComplexFields(Buffer);
case GetMode of
gmCurrent:
Data.GetRecord(Buffer);
gmNext:
Data.GetNextRecord(Buffer);
gmPrior:
Data.GetPriorRecord(Buffer);
end;
if Data.BOF then
Result := grBOF
else
if Data.EOF then
Result := grEOF
else begin
{$IFDEF CLR}
RecInfo := IntPtr(Integer(Buffer) + FRecInfoOfs);
Marshal.WriteInt32(RecInfo, Data.RecordNo); //RecordNumber
Marshal.WriteInt16(RecInfo, sizeof(longint), (byte(bfCurrent) shl 8) or byte(Data.GetUpdateStatus)); //UpdateStatus
if FNeedAddRef then begin
Marshal.WriteByte(RecInfo, sizeof(integer) + sizeof(TUpdateStatus)
+ sizeof(TBookmarkFlag), byte(True)); //RefComplexFields
Data.AddRefComplexFields(Buffer);
end;
{$ELSE}
RecInfo := PRecInfo(Integer(Buffer) + FRecInfoOfs);
RecInfo.RecordNumber := Data.RecordNo;
RecInfo.UpdateStatus := TUpdateStatus(Data.GetUpdateStatus);
RecInfo.BookmarkFlag := bfCurrent;
if FNeedAddRef then begin
RecInfo.RefComplexFields := True;
Data.AddRefComplexFields(Buffer);
end;
{$ENDIF}
//ClearBlobCache(Buffer);
if not FCacheCalcFields then
GetCalcFields(Buffer);
//SetBookmarkFlag(Buffer, bfCurrent);
Data.GetBookmark(PRecBookmark(Integer(Buffer) + FBookmarkOfs));
Result := grOK;
end;
end;
procedure TMemDataSet.InternalInitRecord(Buffer: TRecordBuffer);
begin
//FInDeferredPost := False; moved to InternalInsert
//FInInserting := True;
FreeRefComplexFields(Buffer);
Data.InitRecord(Buffer);
if Data.HasComplexFields then
Data.CreateComplexFields(Buffer, True);
end;
procedure TMemDataSet.InitRecord(Buffer: TRecordBuffer);
var
RecInfo: PRecInfo;
begin
inherited InitRecord(Buffer);
//ClearBlobCache(Buffer);
RecInfo := PRecInfo(integer(Buffer) + FRecInfoOfs);
RecInfo.RecordNumber := 0;
RecInfo.UpdateStatus := TUpdateStatus(usInserted);
RecInfo.BookMarkFlag := bfInserted;
Data.InitRecord(FOldRecBuf); // clear OldRecBuf
end;
function TMemDataSet.GetActiveRecBuf(var RecBuf: TRecordBuffer): boolean;
begin
case State of
dsBlockRead:
if IsEmpty then
RecBuf := nil
else
RecBuf := ActiveBuffer;
dsBrowse:
if FInCacheProcessing then
RecBuf := NewCacheRecBuf
else
if IsEmpty then
RecBuf := nil
else
RecBuf := ActiveBuffer;
dsEdit,dsInsert:
RecBuf := ActiveBuffer;
dsCalcFields:
RecBuf := CalcBuffer;
dsFilter:
RecBuf := FFilterBuffer;
dsNewValue:
if FInCacheProcessing then
RecBuf := NewCacheRecBuf
else
RecBuf := ActiveBuffer;
dsOldValue:
if FInDeferredPost then
RecBuf := OldDeferredPostBuf
else
if FInCacheProcessing then
RecBuf := OldCacheRecBuf
else
RecBuf := GetOldRecord;
// dsSetKey: RecBuf := PChar(FKeyBuffer) + SizeOf(TKeyBuffer);
else
RecBuf := nil;
end;
Result := RecBuf <> nil;
end;
function TMemDataSet.GetOldRecord: TRecordBuffer;
begin
UpdateCursorPos;
Data.GetOldRecord(FOldRecBuf);
Result := FOldRecBuf;
end;
procedure TMemDataSet.ClearCalcFields(Buffer: TRecordBuffer);
var
i: integer;
{$IFNDEF CLR}
{$IFNDEF VER10P}
DataOffset: integer;
{$ENDIF}
{$ENDIF}
begin
if FCreateCalcFieldDescs then begin
for i := 0 to Data.Fields.Count - 1 do
if Data.Fields[i].FieldDescKind <> fdkData then
Data.SetNull(i + 1, Buffer, True);
end
else begin
FillChar(IntPtr(integer(Buffer) + RecordSize), CalcFieldsSize, 0);
{$IFNDEF CLR}
{$IFNDEF VER10P}
DataOffset := 0;
for i := 0 to Fields.Count - 1 do
if (Fields[i].DataType = ftWideString) and (Fields[i].FieldKind in [fkCalculated, fkLookUp]) then begin
Marshal.WriteInt32(Pointer(Integer(Buffer) + RecordSize + Fields[i].Offset + 1), DataOffset);
DataOffset := DataOffset + (Fields[i].Size + 1) * sizeof(WideChar);
end;
FillChar(IntPtr(integer(Buffer) + FWideStringOfs), DataOffset, 0);
{$ENDIF}
{$ENDIF}
end;
end;
// WAR don't support BlockRead
procedure TMemDataSet.SetBlockReadSize(Value: Integer);
begin
if Value <> BlockReadSize then
if (Value > 0) or (Value < -1) then begin
UpdateCursorPos;
inherited;
end
else
inherited;
end;
procedure TMemDataSet.BlockReadNext;
begin
inherited;
end;
{ Bookmarks }
function TMemDataSet.GetBookmarkFlag(Buffer: TRecordBuffer): TBookmarkFlag;
begin
Result := PRecInfo(integer(Buffer) + FRecInfoOfs).BookmarkFlag;
end;
procedure TMemDataSet.SetBookmarkFlag(Buffer: TRecordBuffer; Value: TBookmarkFlag);
begin
PRecInfo(integer(Buffer) + FRecInfoOfs).BookmarkFlag := Value
end;
// Data - pointer to bookmark
procedure TMemDataSet.GetBookmarkData(Buffer: TRecordBuffer;
{$IFDEF CLR}var{$ENDIF} Bookmark: TBookmark);
begin
CopyBuffer(IntPtr(integer(Buffer) + FBookmarkOfs), Bookmark, BookmarkSize);
end;
procedure TMemDataSet.SetBookmarkData(Buffer: TRecordBuffer;
{$IFDEF CLR}const{$ENDIF} Bookmark: TBookmark);
begin
CopyBuffer(Bookmark, IntPtr(integer(Buffer) + FBookmarkOfs), BookmarkSize);
end;
procedure TMemDataSet.InternalGotoBookmark({$IFDEF CLR}const{$ENDIF} Bookmark: TBookmark);
begin
Data.SetToBookMark(PRecBookmark(Bookmark));
end;
{function TMemDataSet.GetBookmarkStr: TBookmarkStr;
begin
if State in [dsFilter] then begin
SetLength(Result, BookmarkSize);
Data.GetBookmark(Pointer(Result));
end
else
Result := inherited GetBookmarkStr;
end;}
function ChangeDecimalSeparator(const Value: string; OldSeparator, NewSeparator: string): string;
begin
if OldSeparator <> NewSeparator then
Result := StringReplace(Value, OldSeparator, NewSeparator, [rfReplaceAll])
else
Result := Value;
end;
function XMLEncode(const AStr: String): String;
var
sb: StringBuilder;
begin
sb := StringBuilder.Create(AStr, Length(AStr));
try
sb.Replace('&', '&#x26;');
sb.Replace('''', '&#x27;');
sb.Replace('"', '&#x22;');
sb.Replace('<', '&#x3c;');
sb.Replace('>', '&#x3e;');
Result := sb.ToString;
finally
sb.Free;
end;
end;
procedure TMemDataSet.SaveToXML(const FileName: string);
var
FileStream: TFileStream;
begin
FileStream := TFileStream.Create(FileName, fmCreate);
try
SaveToXML(FileStream);
finally
FileStream.Free;
end;
end;
procedure TMemDataSet.SaveToXML(Destination: TStream);
var
FieldAliases: TStringList;
function IsValidFieldName(const FldName: string): boolean;
var
i: integer;
ch: char;
begin
for i := 1 to Length(FldName) do begin
ch := FldName[i];
if not ((ch = '_') or (ch >= 'A') and (ch <= 'Z') or (ch >= 'a') and (ch <= 'z') or
(i > 1) and (ch >= '0') and (ch <= '9'))
then begin
Result := False;
Exit;
end;
end;
Result := True;
end;
function FindFieldAlias(FldName: string): string;
var
i, p: integer;
Attr: string;
ActualName: string;
begin
Result := '';
if FieldAliases <> nil then
for i := 0 to FieldAliases.Count - 1 do begin
Attr := FieldAliases[i];
p := Pos('''=c', Attr);
Assert(p <> 0);
ActualName := Copy(Attr, 2, p - 2);
if ActualName = FldName then begin
Result := Copy(Attr, p + 2, Length(Attr) - p - 1);
Break;
end;
end;
end;
var
OldActive: boolean;
Bookmark: DB.TBookmark;
DestWriter: StreamWriter;
XMLWriter: XmlTextWriter;
XML: string;
FldName, FldAlias: string;
i: integer;
FieldDesc: TFieldDesc;
{$IFDEF VER8}
DestStream: Stream;
{$ENDIF}
begin
OldActive := Active;
Bookmark := nil;
FieldAliases := nil;
DestWriter := nil;
XmlWriter := nil;
{$IFDEF VER8}
DestStream := nil;
{$ENDIF}
try
DisableControls;
Active := True;
{$IFDEF CLR}
{$IFDEF VER9P}
{$DEFINE OWNGETBOOKMARK}
{$ENDIF}
{$ENDIF}
{$IFDEF OWNGETBOOKMARK}
if BookmarkAvailable then
begin
Bookmark := Marshal.AllocHGlobal(BookmarkSize);
GetBookmarkData(ActiveBuffer, Bookmark);
end else
Bookmark := nil;
{$ELSE}
Bookmark := GetBookmark;
{$ENDIF}
FieldAliases := TStringList.Create;
{$IFDEF VER8}
DestStream := TStreamToCLRStream.GetStream(Destination);
{$ENDIF}
DestWriter := StreamWriter.Create({$IFDEF VER8}DestStream{$ELSE}Destination{$ENDIF}, Encoding.Default);
XmlWriter := XmlTextWriter.Create(DestWriter);
XmlWriter.QuoteChar := '''';
XmlWriter.Formatting := fmtIndented;
XmlWriter.Indentation := 2;
XmlWriter.IndentChar := ' ';
// Header
XmlWriter.WriteStartElement('xml');
XmlWriter.WriteAttributeString('xmlns:s', 'uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882');
XmlWriter.WriteAttributeString('xmlns:dt', 'uuid:C2F41010-65B3-11d1-A29F-00AA00C14882');
XmlWriter.WriteAttributeString('xmlns:rs', 'urn:schemas-microsoft-com:rowset');
XmlWriter.WriteAttributeString('xmlns:z', '#RowsetSchema');
// Fields
XmlWriter.WriteStartElement('s:Schema');
XmlWriter.WriteAttributeString('id', 'RowsetSchema');
XmlWriter.WriteStartElement('s:ElementType');
XmlWriter.WriteAttributeString('name', 'row');
XmlWriter.WriteAttributeString('content', 'eltOnly');
XmlWriter.WriteAttributeString('rs:updatable', 'true');
for i := 0 to Fields.Count - 1 do
if not (Fields[i].FieldKind in [fkCalculated, fkLookup]) then begin
FieldDesc := GetFieldDesc(Fields[i]);
if FieldDesc <> nil then begin
FldName := FieldDesc.Name;
if not IsValidFieldName(FldName) then begin
FldAlias := 'c' + IntToStr(FieldAliases.Count + 1);
FieldAliases.Add(QuotedStr({$IFNDEF CLR}XMLEncode{$ENDIF}(UTF8Encode(FldName))) + '=' + FldAlias);
end
else
FldAlias := '';
XmlWriter.WriteStartElement('s:AttributeType');
WriteFieldXMLAttributeType(Fields[i], FieldDesc, FldAlias, XmlWriter);
XmlWriter.WriteStartElement('s:datatype');
WriteFieldXMLDataType(Fields[i], FieldDesc, FldAlias, XmlWriter);
XmlWriter.WriteEndElement; // s:datatype
XmlWriter.WriteFullEndElement; // s:AttributeType
end;
end;
XmlWriter.WriteStartElement('s:extends');
XmlWriter.WriteAttributeString('type', 'rs:rowbase');
XmlWriter.WriteEndElement; // s:extends
XmlWriter.WriteFullEndElement; // s:ElementType
XmlWriter.WriteFullEndElement; // s:Schema
// Data
XmlWriter.WriteStartElement('rs:data');
First;
while not EOF do begin
XmlWriter.WriteStartElement('z:row');
for i := 0 to Fields.Count - 1 do
if not (Fields[i].FieldKind in [fkCalculated, fkLookup]) then begin
if not Fields[i].IsNull then begin
XML := GetFieldXMLValue(Fields[i], GetFieldDesc(Fields[i]));
FldName := {$IFNDEF CLR}XMLEncode{$ENDIF}(UTF8Encode(Fields[i].FieldName));
if not IsValidFieldName(FldName) then
FldName := FindFieldAlias(FldName);
XmlWriter.WriteAttributeString(FldName, XML);
end;
end;
XmlWriter.WriteEndElement;
Next;
end;
XmlWriter.WriteFullEndElement; // rs:data
XmlWriter.WriteFullEndElement; // xml
XmlWriter.Close;
finally
Active := OldActive;
if OldActive and (Bookmark <> nil) then
GotoBookmark(Bookmark);
if Bookmark <> nil then
{$IFDEF OWNGETBOOKMARK}
Marshal.FreeHGlobal(Bookmark);
{$ELSE}
FreeBookmark(Bookmark);
{$ENDIF}
FieldAliases.Free;
XmlWriter.Free;
DestWriter.Free;
{$IFDEF VER8}
DestStream.Free;
{$ENDIF}
EnableControls;
end;
end;
procedure TMemDataSet.WriteFieldXMLDataType(Field: TField; FieldDesc: TFieldDesc;
const FieldAlias: string; XmlWriter: XMLTextWriter);
begin
case FieldDesc.DataType of
dtInt64: begin
XmlWriter.WriteAttributeString('dt:type', 'i8');
XmlWriter.WriteAttributeString('dt:maxLength', IntToStr(FieldDesc.Size));
XmlWriter.WriteAttributeString('rs:precision', '19');
end;
dtBlob, dtBytes, dtVarBytes, dtExtVarBytes: begin
XmlWriter.WriteAttributeString('dt:type', 'bin.hex');
if (FieldDesc.DataType = dtBlob) then
XmlWriter.WriteAttributeString('dt:maxLength', '2147483647')
else
XmlWriter.WriteAttributeString('dt:maxLength', IntToStr(FieldDesc.Length));
if Field.IsBlob and not FieldDesc.Fixed then
XmlWriter.WriteAttributeString('rs:long', 'true');
end;
dtBoolean: begin
XmlWriter.WriteAttributeString('dt:type', 'boolean');
XmlWriter.WriteAttributeString('dt:maxLength', IntToStr(FieldDesc.Size));
end;
dtString, dtExtString, dtMemo, dtWideString, dtExtWideString, dtWideMemo: begin
XmlWriter.WriteAttributeString('dt:type', 'string');
if FieldDesc.DataType in [dtMemo, dtWideMemo] then
XmlWriter.WriteAttributeString('dt:maxLength', '2147483647')
else
XmlWriter.WriteAttributeString('dt:maxLength', IntToStr(FieldDesc.Length));
if not (FieldDesc.DataType in [dtWideString, dtExtWideString, dtWideMemo]) then
XmlWriter.WriteAttributeString('rs:dbtype', 'str');
if Field.IsBlob and not FieldDesc.Fixed then
XmlWriter.WriteAttributeString('rs:long', 'true');
end;
dtCurrency, dtBCD: begin
XmlWriter.WriteAttributeString('dt:type', 'number');
XmlWriter.WriteAttributeString('rs:dbtype', 'currency');
XmlWriter.WriteAttributeString('dt:maxLength', IntToStr(FieldDesc.Size));
XmlWriter.WriteAttributeString('rs:precision', IntToStr(FieldDesc.Length));
end;
dtDateTime: begin
XmlWriter.WriteAttributeString('dt:type', 'datetime');
XmlWriter.WriteAttributeString('rs:dbtype', 'variantdate');
XmlWriter.WriteAttributeString('dt:maxLength', IntToStr(FieldDesc.Size));
XmlWriter.WriteAttributeString('rs:scale', IntToStr(FieldDesc.Scale));
XmlWriter.WriteAttributeString('rs:precision', IntToStr(FieldDesc.Length));
end;
dtDate: begin
XmlWriter.WriteAttributeString('dt:type', 'date');
XmlWriter.WriteAttributeString('dt:maxLength', IntToStr(FieldDesc.Size));
XmlWriter.WriteAttributeString('rs:scale', IntToStr(FieldDesc.Scale));
XmlWriter.WriteAttributeString('rs:precision', IntToStr(FieldDesc.Length));
end;
dtTime: begin
XmlWriter.WriteAttributeString('dt:type', 'time');
XmlWriter.WriteAttributeString('dt:maxLength', IntToStr(FieldDesc.Size));
XmlWriter.WriteAttributeString('rs:scale', IntToStr(FieldDesc.Scale));
XmlWriter.WriteAttributeString('rs:precision', IntToStr(FieldDesc.Length));
end;
dtFloat: begin
if FieldDesc.Length <= 7 then begin
XmlWriter.WriteAttributeString('dt:type', 'r4');
XmlWriter.WriteAttributeString('dt:maxLength', '4');
end
else begin
XmlWriter.WriteAttributeString('dt:type', 'float');
XmlWriter.WriteAttributeString('dt:maxLength', IntToStr(FieldDesc.Size));
end;
XmlWriter.WriteAttributeString('rs:precision', IntToStr(FieldDesc.Length));
end;
{$IFDEF VER5P}
dtGuid: begin
XmlWriter.WriteAttributeString('dt:type', 'uuid');
XmlWriter.WriteAttributeString('dt:maxLength', '16');
end;
{$ENDIF}
dtInt32: begin
XmlWriter.WriteAttributeString('dt:type', 'int');
XmlWriter.WriteAttributeString('dt:maxLength', IntToStr(FieldDesc.Size));
XmlWriter.WriteAttributeString('rs:precision', '10');
end;
{$IFDEF VER6P}
dtFmtBCD: begin
XmlWriter.WriteAttributeString('dt:type', 'number');
XmlWriter.WriteAttributeString('rs:dbtype', 'numeric');
XmlWriter.WriteAttributeString('dt:maxLength', IntToStr(FieldDesc.Size));
XmlWriter.WriteAttributeString('rs:scale', IntToStr(FieldDesc.Scale));
XmlWriter.WriteAttributeString('rs:precision', IntToStr(FieldDesc.Length));
end;
{$ENDIF}
dtInt16: begin
XmlWriter.WriteAttributeString('dt:type', 'i2');
XmlWriter.WriteAttributeString('dt:maxLength', IntToStr(FieldDesc.Size));
XmlWriter.WriteAttributeString('rs:precision', '5');
end;
dtInt8: begin
XmlWriter.WriteAttributeString('dt:type', 'i1');
XmlWriter.WriteAttributeString('dt:maxLength', IntToStr(FieldDesc.Size));
XmlWriter.WriteAttributeString('rs:precision', IntToStr(FieldDesc.Length));
end;
dtLongword: begin
XmlWriter.WriteAttributeString('dt:type', 'ui4');
XmlWriter.WriteAttributeString('dt:maxLength', IntToStr(FieldDesc.Size));
XmlWriter.WriteAttributeString('rs:precision', IntToStr(FieldDesc.Length));
end;
dtWord: begin
XmlWriter.WriteAttributeString('dt:type', 'ui2');
XmlWriter.WriteAttributeString('dt:maxLength', IntToStr(FieldDesc.Size));
XmlWriter.WriteAttributeString('rs:precision', IntToStr(FieldDesc.Length));
end;
dtUnknown{$IFDEF VER5P}, dtVariant{$ENDIF}:
XmlWriter.WriteAttributeString('dt:type', 'string')
else
DatabaseError(SDataTypeNotSupported, Self);
end;
if FieldDesc.Fixed and not (FieldDesc.DataType in [dtUnknown{$IFDEF VER5P}, dtVariant{$ENDIF}]) then
XmlWriter.WriteAttributeString('rs:fixedlength', 'true');
if Field.Required and not Field.ReadOnly then
XmlWriter.WriteAttributeString('rs:maybenull', 'false');
end;
procedure TMemDataSet.WriteFieldXMLAttributeType(Field: TField; FieldDesc: TFieldDesc;
const FieldAlias: string; XmlWriter: XMLTextWriter);
begin
if FieldAlias = '' then
XmlWriter.WriteAttributeString('name', {$IFNDEF CLR}XMLEncode{$ENDIF}(UTF8Encode(FieldDesc.Name)))
else begin
XmlWriter.WriteAttributeString('name', FieldAlias);
XmlWriter.WriteAttributeString('rs:name', {$IFNDEF CLR}XMLEncode{$ENDIF}(UTF8Encode(FieldDesc.Name)));
end;
XmlWriter.WriteAttributeString('rs:number', IntToStr(FieldDesc.FieldNo));
if not Field.Required and not Field.ReadOnly then /// Can't use FieldDesc.Required, see "Required and FLocalConstraints" line in TMemDataSet.CreateFieldDefs for details
XmlWriter.WriteAttributeString('rs:nullable', 'true');
if not Field.ReadOnly then
XmlWriter.WriteAttributeString('rs:writeunknown', 'true');
// XmlWriter.WriteAttributeString('rs:basecatalog', '');
if FieldDesc.ActualName <> '' then
XmlWriter.WriteAttributeString('rs:basecolumn', {$IFNDEF CLR}XMLEncode{$ENDIF}(UTF8Encode(FieldDesc.ActualName)));
if FieldDesc.IsKey then
XmlWriter.WriteAttributeString('rs:keycolumn', 'true');
if Field.AutoGenerateValue = arAutoInc then
XmlWriter.WriteAttributeString('rs:autoincrement', 'true');
end;
function TMemDataSet.GetFieldXMLValue(Field: TField; FieldDesc: TFieldDesc): string;
var
Buffer: TBytes;
Blob: TBlob;
Piece: PPieceHeader;
{$IFDEF CLR}
sb: StringBuilder;
Bytes: TBytes;
{$ELSE}
sbOffset: integer;
{$ENDIF}
function EncodeXMLDateTime(Value: TDateTime): string;
var
Year, Month, Day, Hour, Minute, Second, MilliSecond: Word;
begin
DecodeDateTime(Value, Year, Month, Day, Hour, Minute, Second, MilliSecond);
Result := StringReplace(Format('%4d%s%2d%s%2d', [Year, '-', Month, '-', Day]),
' ', '0', [rfReplaceAll]);
Result := Result + 'T';
Result := StringReplace(Result + Format('%2d%s%2d%s%2d', [Hour, ':', Minute, ':', Second]),
' ', '0', [rfReplaceAll]);
end;
function EncodeXMLTime(Value: TDateTime): string;
var
Hour, Minute, Second, MilliSecond: Word;
begin
DecodeTime(Value, Hour, Minute, Second, MilliSecond);
Result := StringReplace(Format('%2d%s%2d%s%2d', [Hour, ':', Minute, ':', Second]),
' ', '0', [rfReplaceAll]);
end;
begin
Result := '';
SetLength(Buffer, 0);
case FieldDesc.DataType of
dtBoolean:
Result := BoolToStr(Field.AsBoolean, True);
dtInt8, dtInt16, dtInt32, dtUInt16, dtUInt32, dtInt64:
Result := Field.AsString;
dtFloat:
Result := ChangeDecimalSeparator(FloatToStr(Field.AsFloat), DecimalSeparator, '.');
dtCurrency, dtBcd:
Result := ChangeDecimalSeparator(CurrToStr(Field.Value), DecimalSeparator, '.');
dtDate, dtDateTime:
Result := EncodeXMLDateTime(Field.AsDateTime);
dtTime:
Result := EncodeXMLTime(Field.AsDateTime);
{$IFDEF VER6P}
dtFmtBCD:
Result := BcdToStr(Field.AsBCD);
{$ENDIF}
dtBytes, dtVarBytes, dtExtVarBytes: begin
Buffer := Field.Value;
SetLength(Result, Length(Buffer) * 2);
{$IFDEF CLR}
Bytes := Encoding.Default.GetBytes(Result);
BinToHex(Buffer, 0, Bytes, 0, Length(Buffer));
Result := Encoding.Default.GetString(Bytes);
{$ELSE}
BinToHex(@Buffer[0], PChar(Result), Length(Buffer));
{$ENDIF}
end;
dtBlob: begin
Blob := InternalGetBlob(FieldDesc);
SetLength(Result, Integer(Blob.Size) * 2);
Piece := Blob.FirstPiece;
{$IFDEF CLR}
sb := StringBuilder.Create;
try
while IntPtr(Piece) <> nil do begin
SetLength(Buffer, Piece.Used);
Marshal.Copy(IntPtr(Integer(Piece) + sizeof(TPieceHeader)), Buffer, 0, Piece.Used);
SetLength(Bytes, Length(Buffer) * 2);
BinToHex(Buffer, 0, Bytes, 0, Length(Buffer));
sb.Append(Encoding.Default.GetString(Bytes));
Piece := Piece.Next;
end;
Result := sb.ToString;
finally
sb.Free;
end;
{$ELSE}
sbOffset := 0;
while Piece <> nil do begin
BinToHex(IntPtr(Integer(Piece) + sizeof(TPieceHeader)),
IntPtr(Integer(Result) + sbOffset), Piece.Used);
sbOffset := sbOffset + Integer(Piece.Used);
Piece := Piece.Next;
end;
{$ENDIF}
end;
else
Result := Field.AsString;
end;
Result := {$IFNDEF CLR}XMLEncode{$ENDIF}(UTF8Encode(Result));
end;
{$IFDEF CLR} // TDataSet bug
var
NativeDBBuffers: TDBBufferList;
function NativeBuffers: TDBBufferList;
begin
if NativeDBBuffers <> nil then
Result := NativeDBBuffers
else
begin
Result := TDBBufferList.Create;
NativeDBBuffers := Result; // fix d9 bug
end;
end;
function TMemDataSet.GetBookmark: TBookmark;
begin
if BookmarkAvailable then
begin
Result := NativeBuffers.AllocHGlobal(BookmarkSize);
GetBookmarkData(ActiveBuffer, Result);
end else
Result := nil;
end;
procedure TMemDataSet.FreeBookmark(var Bookmark: TBookmark);
begin
NativeBuffers.FreeHGlobal(Bookmark);
end;
{$ENDIF}
function TMemDataSet.BookmarkValid({$IFDEF CLR}const{$ENDIF} Bookmark: TBookmark): boolean;
begin
Result := Data.BookmarkValid(PRecBookmark(Bookmark));
end;
function TMemDataSet.CompareBookmarks({$IFDEF CLR}const{$ENDIF} Bookmark1, Bookmark2: TBookmark): integer;
begin
Result := Data.CompareBookmarks(PRecBookmark(Bookmark1), PRecBookmark(Bookmark2));
end;
{ Navigation }
procedure TMemDataSet.InternalSetToRecord(Buffer: TRecordBuffer);
begin
InternalGotoBookmark(IntPtr(integer(Buffer) + FBookmarkOfs));
end;
procedure TMemDataSet.SetIndexFieldNames(Value: string);
begin
if Active then
CheckBrowseMode;
TMemData(Data).SetIndexFieldNames(Value);
if Active and (TMemData(Data).IndexFields.Count > 0) then
Resync([]);
FIndexFieldNames := Value;
end;
procedure TMemDataSet.InternalFirst;
begin
Data.SetToBegin;
end;
procedure TMemDataSet.InternalLast;
begin
Data.SetToEnd;
end;
{ Editing }
procedure TMemDataSet.InternalAddRecord(Buffer: IntPtr; Append: Boolean);
begin
if Append then
Data.AppendRecord(Buffer)
else
Data.InsertRecord(Buffer);
end;
procedure TMemDataSet.InternalInsert;
begin
FInDeferredPost := False;
if OldDeferredPostBuf <> nil then begin
FreeRefComplexFields(OldDeferredPostBuf, False);
Marshal.FreeHGlobal(OldDeferredPostBuf);
OldDeferredPostBuf := nil;
end;
FInInserting := True;
end;
procedure TMemDataSet.InternalEdit;
begin
FInDeferredPost := False;
if OldDeferredPostBuf <> nil then begin
FreeRefComplexFields(OldDeferredPostBuf, False);
Marshal.FreeHGlobal(OldDeferredPostBuf);
OldDeferredPostBuf := nil;
end;
FInEditing := True;
FreeRefComplexFields(ActiveBuffer);
Data.EditRecord(ActiveBuffer);
end;
procedure TMemDataSet.InternalDelete;
begin
if not CanModify then DatabaseError(SDataSetReadOnly, Self);
Data.DeleteRecord;
// CR M8107
FInInserting := False;
FInEditing := False;
end;
procedure TMemDataSet.InternalPost;
var
i: integer;
Blob: TBlob;
Field: TField;
FieldDesc: TFieldDesc;
begin
{$IFDEF VER6P}
inherited;
{$ENDIF}
for i := 0 to FieldCount - 1 do begin
Field := Fields[i];
FieldDesc := GetFieldDesc(Field);
if (FieldDesc <> nil) and Data.IsBlobFieldType(FieldDesc.DataType) then begin
Blob := Data.GetObject(FieldDesc.FieldNo, ActiveBuffer) as TBlob;
if Blob.Modified then
TBlobField(Field).Modified := True;
end;
end;
if State = dsEdit then begin
Data.PostRecord(ActiveBuffer);
end
else
Data.InsertRecord(ActiveBuffer);
FInDeferredPost := False;
if OldDeferredPostBuf <> nil then begin
FreeRefComplexFields(OldDeferredPostBuf, False);
Marshal.FreeHGlobal(OldDeferredPostBuf);
OldDeferredPostBuf := nil;
end;
FInInserting := False;
FInEditing := False;
end;
procedure TMemDataSet.Cancel;
var
CancelBuf : IntPtr;
begin
if ((State = dsEdit) or (State = dsInsert)) and (Data.HasComplexFields) then begin
CancelBuf := AllocRecordBuffer;
CopyBuffer(ActiveBuffer, CancelBuf, FRecBufSize);
Data.AddRefComplexFields(CancelBuf);
end
else
CancelBuf := nil;
try
{$IFDEF D3}
if State = dsInsert then
if Data.HasComplexFields then
Data.FreeComplexFields(ActiveBuffer, True);
{$ENDIF}
inherited;
finally
if CancelBuf <> nil then begin
Data.FreeComplexFields(CancelBuf, True);
Marshal.FreeHGlobal(CancelBuf);
end;
end;
end;
procedure TMemDataSet.InternalCancel;
begin
FInDeferredPost := False;
if OldDeferredPostBuf <> nil then begin
FreeRefComplexFields(OldDeferredPostBuf, False);
Marshal.FreeHGlobal(OldDeferredPostBuf);
OldDeferredPostBuf := nil;
end;
FInInserting := False;
FInEditing := False;
if State = dsEdit then
Data.CancelRecord(ActiveBuffer);
{$IFNDEF D3}
if State = dsInsert then
if Data.HasComplexFields then
Data.FreeComplexFields(ActiveBuffer, True);
{$ENDIF}
end;
procedure TMemDataSet.InternalDeferredPost;
begin
if State = dsEdit then
DoPerformUpdate
else
DoPerformAppend;
end;
procedure TMemDataSet.DeferredPost;
procedure CheckRequiredFields;
var
I: Integer;
begin
for I := 0 to Fields.Count - 1 do
with Fields[I] do
if Required and not ReadOnly and (FieldKind = fkData) and IsNull then
begin
FocusControl;
DatabaseErrorFmt(SFieldRequired, [DisplayName]);
end;
end;
begin
if not CachedUpdates then begin
UpdateRecord;
case State of
dsEdit, dsInsert:
begin
{$IFDEF CLR}
DataEvent(deCheckBrowseMode, nil);
{$ELSE}
DataEvent(deCheckBrowseMode, 0);
{$ENDIF}
CheckRequiredFields;
UpdateCursorPos;
InternalDeferredPost;
if OldDeferredPostBuf = nil then begin
OldDeferredPostBuf := AllocRecordBuffer;
Data.CreateComplexFields(OldDeferredPostBuf, False);
PRecInfo(Integer(OldDeferredPostBuf) + FRecInfoOfs).RefComplexFields := True;//own complex fields
end;
CopyRecordBuffer(ActiveBuffer, OldDeferredPostBuf);
FInDeferredPost := True;
end;
end;
end;
end;
procedure TMemDataSet.SetDefaultExpressionValue(Field: TField);
begin
Field.AsString := Field.DefaultExpression;
end;
procedure TMemDataSet.DoOnNewRecord;
var
i: integer;
begin
for i := 0 to FieldCount - 1 do
if Fields[i].DefaultExpression <> '' then
SetDefaultExpressionValue(Fields[i]);
try
inherited;
except
InternalCancel;
raise;
end;
end;
function TMemDataSet.PerformAppend: boolean;
begin
Result := False;
end;
function TMemDataSet.PerformDelete: boolean;
begin
Result := False;
end;
function TMemDataSet.PerformUpdate: boolean;
begin
Result := False;
end;
procedure TMemDataSet.DoPerformAppend;
var
OldModified: boolean;
begin
OldModified := Modified;
try
if not FLocalUpdate then
if not FInDeferredPost then // WAR supports defer posting
PerformAppend
else
DoPerformUpdate;
finally
SetModified(OldModified);
end;
end;
procedure TMemDataSet.DoPerformDelete;
var
OldModified: boolean;
begin
OldModified := Modified;
try
if not FLocalUpdate then
PerformDelete
finally
SetModified(OldModified);
end;
end;
procedure TMemDataSet.DoPerformUpdate;
var
OldModified: boolean;
begin
OldModified := Modified;
try
if not FLocalUpdate then
PerformUpdate
finally
SetModified(OldModified);
end;
end;
{ Filter / Locate / Find }
procedure TMemDataSet.DoGetCachedFields;
var
i: Integer;
FieldDesc: TFieldDesc;
CalcFieldCount: integer;
begin
CalcFieldCount := 0;
if not DefaultFields then begin
for i := 0 to Fields.Count - 1 do
case Fields[i].FieldKind of
fkCalculated, fkLookup: begin
FieldDesc := Data.GetFieldDescType.Create;
try
FieldDesc.ActualName := Fields[i].FullName;
FieldDesc.Name := Fields[i].FullName;
FieldDesc.FieldNo := Data.FieldCount + 1;
FieldDesc.DataType := GetDataType(Fields[i].DataType);
case Fields[i].DataType of
ftWideString:
FieldDesc.Size := (Fields[i].Size + 1) * SizeOf(WideChar);
ftDate, ftTime:
FieldDesc.Size := Fields[i].DataSize * 2;
else
FieldDesc.Size := Fields[i].DataSize;
end;
FieldDesc.Length := Fields[i].Size;
if FCacheCalcFields then
FieldDesc.FieldDescKind := fdkCached
else
FieldDesc.FieldDescKind := fdkCalculated;
// FieldDescs with CachedField=True must be positioned after data FieldDescs
Data.Fields.Add(FieldDesc);
Inc(CalcFieldCount);
SetLength(FCalcFieldsMapping, CalcFieldCount);
FCalcFieldsMapping[CalcFieldCount - 1].Field := Fields[i];
FCalcFieldsMapping[CalcFieldCount - 1].FieldDesc := FieldDesc;
except
FieldDesc.Free;
raise;
end;
end;
end;
end;
end;
procedure TMemDataSet.DoGetCachedBuffer(Buffer: IntPtr; Source: IntPtr = nil);
var
RecBuf: IntPtr;
begin
if (CalcFieldsSize > 0) then begin
RecBuf := Marshal.AllocHGlobal(Data.RecordSize + Data.CalcRecordSize + SizeOf(TRecInfo) + BookmarkSize);
try
if Source = nil then
CopyBuffer(Buffer, RecBuf, Data.RecordSize)
else
CopyBuffer(Source, RecBuf, Data.RecordSize);
GetCalcFields(RecBuf);
CopyBuffer(RecBuf, Buffer, Data.RecordSize + Data.CalcRecordSize);
finally
Marshal.FreeHGlobal(RecBuf);
end;
end;
end;
procedure TMemDataSet.ActivateFilters;
begin
DeactivateFilters;
if Assigned(OnFilterRecord) then
Data.FilterFunc := RecordFilter;
if Trim(Filter) <> '' then
Data.FilterText := Filter;
end;
procedure TMemDataSet.DeactivateFilters;
begin
Data.FilterFunc := nil;
Data.FilterText := '';
end;
function TMemDataSet.RecordFilter(RecBuf: IntPtr): boolean;
var
Accept: boolean;
SaveState: TDataSetState;
begin
SaveState := SetTempState(dsFilter);
FFilterBuffer := RecBuf;
try
Accept := True;
OnFilterRecord(Self, Accept);
except
InternalHandleException;
end;
RestoreState(SaveState);
Result := Accept;
end;
procedure TMemDataSet.SetFiltered(Value: boolean);
begin
if Active then begin
CheckBrowseMode;
UpdateCursorPos;
end;
if Value <> Filtered then begin
if Value then
ActivateFilters
else
DeactivateFilters;
inherited SetFiltered(Value);
if Active then begin
Data.FilterUpdated;
Resync([]);
First;
//DoAfterScroll;
end;
end;
end;
procedure TMemDataSet.SetFilterData(const Text: string; Options: TFilterOptions);
begin
if Active then begin
CheckBrowseMode;
UpdateCursorPos;
end;
if (Text <> Filter) or (Options <> FilterOptions) then begin
Data.FilterCaseInsensitive := foCaseInsensitive in Options;
Data.FilterNoPartialCompare := foNoPartialCompare in Options;
if Filtered and (Trim(Text) <> '') then
Data.FilterText := Text
else
Data.FilterText := '';
inherited SetFilterText(Text);
inherited SetFilterOptions(Options);
if Active and Filtered then begin
Data.FilterUpdated;
Resync([]);
First;
//DoAfterScroll;
end;
end;
end;
procedure TMemDataSet.SetFilterOptions(Value: TFilterOptions);
begin
SetFilterData(Filter, Value);
end;
procedure TMemDataSet.SetFilterText(const Value: string);
begin
SetFilterData(Value, FilterOptions);
end;
procedure TMemDataSet.SetOnFilterRecord(const Value: TFilterRecordEvent);
begin
if Active then begin
CheckBrowseMode;
UpdateCursorPos;
end;
inherited SetOnFilterRecord(Value);
if Filtered and Assigned(OnFilterRecord) then
Data.FilterFunc := RecordFilter
else
Data.FilterFunc := nil;
if Active then begin
Data.FilterUpdated;
Resync([]);
First;
end;
end;
function TMemDataSet.FindRecord(Restart, GoForward: boolean): boolean;
begin
CheckBrowseMode;
DoBeforeScroll;
SetFound(False);
UpdateCursorPos;
CursorPosChanged;
if not Filtered then
ActivateFilters;
try
if GoForward then begin
if Restart then
Data.SetToBegin;
Data.GetNextRecord(nil);
end
else begin
if Restart then
Data.SetToEnd;
Data.GetPriorRecord(nil);
end;
finally
if not Filtered then
DeactivateFilters;
end;
if not Data.BOF and not Data.EOF then begin
Resync([rmExact, rmCenter]);
SetFound(True);
end;
Result := Found;
if Result then
DoAfterScroll;
end;
// Allocate memory for Value (ValuePtr - must call FreeMem!) and copy Value to ValuePtr
procedure TMemDataSet.CopyFieldValue(const Value: variant; out ValuePtr: IntPtr;
out ValueType: integer; FieldDesc: TFieldDesc); // Allocate memory for Value (ValuePtr - must call FreeMem!) and copy Value to ValuePtr
var
BoolValue: boolean;
s: string;
l: integer;
Temp: IntPtr;
ws: WideString;
{$IFDEF CLR}
Data: TBytes;
{$ENDIF}
{$IFDEF VER6P}
i64: Int64;
bcd: TBcd;
{$ENDIF}
begin
case VarType(Value) of
varEmpty,varNull:
ValuePtr := nil;
varString{$IFDEF CLR}, varChar{$ELSE}, varOleStr{$ENDIF}:
case FieldDesc.DataType of
dtBoolean: begin
BoolValue := Value;
ValuePtr := Marshal.AllocHGlobal(SizeOf(Boolean));
Marshal.WriteByte(ValuePtr, integer(BoolValue));
ValueType := dtBoolean;
end;
dtBytes, dtVarBytes, dtExtVarBytes: begin
s := Value;
l := Length(s);
ValuePtr := Marshal.AllocHGlobal(l + SizeOf(Word));
Temp := Marshal.StringToHGlobalAnsi(s);
try
CopyBuffer(Temp, IntPtr(integer(ValuePtr) + SizeOf(Word)), l);
finally
Marshal.FreeCoTaskMem(Temp);
end;
Marshal.WriteInt16(ValuePtr, l);
ValueType := FieldDesc.DataType;
end;
dtWideString:
begin
ws := Value;
l := (Length(ws) + 1) * SizeOf(WideChar);
ValuePtr := Marshal.AllocHGlobal(l);
CopyBufferUni(ws, ValuePtr, l);
ValueType := dtWideString;
end;
else
begin
s := Value;
l := Length(s) + 1;
ValuePtr := Marshal.AllocHGlobal(l);
CopyBufferAnsi(s, ValuePtr, l);
ValueType := dtString;
end;
end;
else
ValueType := FieldDesc.DataType;
case ValueType of
dtInt32, dtInt8, dtInt16, dtUInt16: begin
ValuePtr := Marshal.AllocHGlobal(SizeOf(integer));
Marshal.WriteInt32(ValuePtr, Value);
end;
dtUInt32: begin
ValuePtr := Marshal.AllocHGlobal(SizeOf(longword));
Marshal.WriteInt32(ValuePtr, Value);
end;
dtBoolean: begin
ValuePtr := Marshal.AllocHGlobal(SizeOf(Boolean));
Marshal.WriteByte(ValuePtr, Integer(Boolean(Value)));
end;
dtInt64: begin
{$IFDEF VER6P}
ValuePtr := Marshal.AllocHGlobal(SizeOf(Int64));
i64 := Value;
Marshal.WriteInt64(ValuePtr, i64);
{$ELSE}
GetMem(ValuePtr, SizeOf(Int64));
if TVarData(Value).VType = varDecimal then
Int64(ValuePtr^) := TVarDataD6(Value).VInt64
else
Int64(ValuePtr^) := StrToInt64(Value);
{$ENDIF}
end;
dtFloat, dtCurrency: begin
ValuePtr := Marshal.AllocHGlobal(SizeOf(Int64));
Marshal.WriteInt64(ValuePtr, BitConverter.DoubleToInt64Bits(Value));
end;
dtBCD: begin
ValuePtr := Marshal.AllocHGlobal(SizeOf(Currency));
Marshal.WriteInt64(ValuePtr, BitConverter.DoubleToInt64Bits(Value));
end;
{$IFDEF VER6P}
dtFmtBCD: begin
ValuePtr := Marshal.AllocHGlobal(SizeOfTBcd);
bcd := VarToBcd(Value);
{$IFDEF CLR}
Data := TBcd.ToBytes(bcd);
Marshal.Copy(Data, 0, ValuePtr, SizeOfTBcd);
{$ELSE}
PBcd(ValuePtr)^ := bcd;
{$ENDIF}
end;
{$ENDIF}
dtString, dtExtString: begin
s := Value;
l := Length(s) + 1;
ValuePtr := Marshal.AllocHGlobal(l);
CopyBufferAnsi(s, ValuePtr, l);
ValueType := dtString;
end;
dtWideString, dtExtWideString: begin
ws := Value;
l := (Length(ws) + 1) * SizeOf(WideChar);
ValuePtr := Marshal.AllocHGlobal(l);
CopyBufferUni(ws, ValuePtr, l);
ValueType := dtWideString;
end;
dtDateTime, dtDate, dtTime: begin
ValuePtr := Marshal.AllocHGlobal(SizeOf(Int64));
Marshal.WriteInt64(ValuePtr, BitConverter.DoubleToInt64Bits(TDateTime(Value)));
end;
dtBytes, dtVarBytes, dtExtVarBytes: begin
Assert(VarType(Value) = varArray + varByte);
{$IFDEF CLR}
SetLength(Data, VarArrayHighBound(Value, 1) + 1);
for l := 0 to High(Data) do
Data[l] := VarArrayGet(Value, l);
ValuePtr := Marshal.AllocHGlobal(Length(Data) + sizeof(word));
Marshal.WriteInt16(ValuePtr, Length(Data));
Marshal.Copy(Data, 0, IntPtr(integer(ValuePtr) + sizeof(word)), Length(Data));
{$ELSE}
GetMem(ValuePtr, TVarData(Value).VArray.Bounds[0].ElementCount + SizeOf(Word));
Move(TVarData(Value).VArray.Data^, (PChar(ValuePtr) + SizeOf(Word))^, TVarData(Value).VArray.Bounds[0].ElementCount);
Word(ValuePtr^) := TVarData(Value).VArray.Bounds[0].ElementCount;
{$ENDIF}
end;
else
raise EConvertError.Create(SCannotConvertType);
end;
end;
end;
function TMemDataSet.InternalLocateRecord(KeyFields: TDAList; const KeyValues: variant;
Options: TLocateExOptions; SavePos: boolean): boolean;
var
FieldDesc: TFieldDesc;
RecBuf: TRecordBuffer;
Values: array of IntPtr;
Types: array of integer;
i, FieldCount: integer;
IndexedFieldCount: integer;
Bookmark: PRecBookmark;
Res: integer;
CalcKeyFields: boolean;
FirstRecNo, LastRecNo: Integer;
procedure SetKeyFields;
var
i: integer;
Index: integer;
TmpValue: IntPtr;
TmpType: integer;
Value: variant;
begin
CalcKeyFields := False;
FieldCount := KeyFields.Count;
SetLength(Values, FieldCount);
for i := 0 to FieldCount - 1 do begin
Values[i] := nil; // Clear Values array to prevent AV in 'finally' section after Exception
if TFieldDesc(KeyFields[i]).FieldDescKind = fdkCalculated then
CalcKeyFields := True;
end;
SetLength(Types, FieldCount);
for i := 0 to FieldCount - 1 do begin
Value := Unassigned;
if VarIsArray(KeyValues)
and ((FieldCount > 1) or (VarArrayHighBound(KeyValues, 1) = 0)) then
if i <= VarArrayHighBound(KeyValues, 1) then
Value := KeyValues[i]
else
Value := Null
else
if i = 0 then
Value := KeyValues
else
Value := Null;
CopyFieldValue(Value, Values[i], Types[i], TFieldDesc(KeyFields[i]));
end;
IndexedFieldCount := 0;
if not ((lxPartialKey in Options) or (lxPartialCompare in Options) or (lxNearest in Options))then //lxPartialKey and lxPartialCompare incompatible with ordered locate
//lxNearest can be partially supported in the next versions
for i := 0 to TMemData(Data).IndexFields.Count - 1 do begin
//Check ordered locate posibility for current Index field
if (not TMemData(Data).IndexFields[i].CaseSensitive) xor
(lxCaseInsensitive in Options) then
break;
//First IndexFields should be in KeyFields if not then we can't use ordered Locate
Index := KeyFields.IndexOf(TMemData(Data).IndexFields[i].FieldDesc);
if (Index > -1) then begin
if IndexedFieldCount <> Index then begin
FieldDesc := TFieldDesc(KeyFields[IndexedFieldCount]);
KeyFields[IndexedFieldCount] := KeyFields[Index];
KeyFields[Index] := FieldDesc;
TmpValue := Values[IndexedFieldCount];
Values[IndexedFieldCount] := Values[Index];
Values[Index] := TmpValue;
TmpType := Types[IndexedFieldCount];
Types[IndexedFieldCount] := Types[Index];
Types[Index] := TmpType;
end;
inc(IndexedFieldCount);
end
else
break;
end;
end;
function GetGoldenSectionDir(ReadRecBuf: boolean): integer;
var
i: integer;
FieldBlank: boolean;
Dir: Integer;
begin
Result := 0;
i := 0;
if ReadRecBuf then
Data.GetRecord(RecBuf);
if CalcKeyFields then
GetCalcFields(RecBuf);
while (Result = 0) and (i < IndexedFieldCount) do begin
FieldDesc := TFieldDesc(KeyFields[i]);
FieldBlank := TMemData(Data).GetNull(FieldDesc.FieldNo, RecBuf);
if TMemData(Data).IndexFields[i].DescendingOrder then
Dir := -1
else
Dir := 1;
if FieldBlank and (Values[i] = nil) then
Result := 0
else
if FieldBlank and not (Values[i] = nil) then
Result := 1
else
if not FieldBlank and (Values[i] = nil) then
Result := -1
else
Result := TMemData(Data).CompareFieldValue(Values[i], Types[i], FieldDesc, RecBuf, Options);
if Result <> 0 then
Result := Result * Dir;
Inc(i);
end;
end;
function ExecGoldenSection: boolean;
var
First, Last, Current: Integer;
Dir: Integer;
begin
Result := False;
First := FirstRecNo;
Last := LastRecNo;
Data.RecordNo := First;
Dir := GetGoldenSectionDir(True);
if Dir <= 0 then begin
Result := Dir = 0;
Exit;
end;
Data.RecordNo := Last;
Dir := GetGoldenSectionDir(True);
if Dir >= 0 then begin
Result := Dir = 0;
Exit;
end;
repeat
Current := (Last + First) div 2;
TMemData(Data).RecordNo := Current;
Dir := GetGoldenSectionDir(True);
if Dir < 0 then
Last := Current
else
if Dir > 0 then
First := Current
else begin
Result := True;
Break;
end;
until Last - First <= 1;
end;
begin
Result := False;
if KeyFields.Count = 0 then
Exit;
CheckBrowseMode;
CursorPosChanged;
UpdateCursorPos;
FieldCount := 0;
RecBuf := TempBuffer;
FreeRefComplexFields(RecBuf);
Values := nil;
Bookmark := Marshal.AllocHGlobal(sizeof(TRecBookmark));
try
SetKeyFields;
Data.GetBookmark(Bookmark);
//Set locate dimensions. This dimensions should be used for lxNearest ordered implementation
FirstRecNo := 1;
LastRecNo := Data.RecordCount;
if (lxNext in Options) then begin//Search from current position
if IndexedFieldCount > 0 then
Data.GetNextRecord(RecBuf); //Next RecNo in case of Ordered Search
FirstRecNo := Data.RecordNo;
end;
if (lxUp in Options) then begin //Search from current position downto first
if IndexedFieldCount > 0 then
Data.GetPriorRecord(RecBuf); //Prior RecNo in case of Ordered Search
LastRecNo := Data.RecordNo;
end;
if IndexedFieldCount > 0 then begin
TMemData(Data).PrepareRecNoCache;
Result := ExecGoldenSection;
//Find the first occurence of located data
if Result then
repeat
if (lxUp in Options) then begin
if Data.RecordNo >= LastRecNo then //top limit of first occurrence search
break
else begin
Data.GetNextRecord(RecBuf);
if Data.Eof or (GetGoldenSectionDir(False) <> 0) then begin
if IndexedFieldCount >= FieldCount then //we shouldn't restore correct position in case of mixed locate
Data.GetPriorRecord(RecBuf); //Restore correct position
break;
end;
end;
end
else
if Data.RecordNo <= FirstRecNo then //bottom limit of first occurrence search
break
else begin
Data.GetPriorRecord(RecBuf);
if (GetGoldenSectionDir(False) <> 0) then begin
if IndexedFieldCount >= FieldCount then //we shouldn't restore correct position in case of mixed locate
Data.GetNextRecord(RecBuf); //Restore correct position
break;
end;
end;
until Data.Bof or Data.Eof;
end;
if (IndexedFieldCount = 0) or ((IndexedFieldCount < FieldCount) and Result) then begin
if not((lxNext in Options) or (lxUp in Options)) and not (IndexedFieldCount > 0) then
Data.SetToBegin;
while True do begin
if lxUp in Options then
Data.GetPriorRecord(RecBuf)
else
Data.GetNextRecord(RecBuf);
if CalcKeyFields then
GetCalcFields(RecBuf);
if not (Data.EOF or Data.BOF) then begin
Result := True;
i := 0;
while Result and (i < FieldCount) do begin
FieldDesc := TFieldDesc(KeyFields[i]);
if (Values[i] = nil) or Data.GetNull(FieldDesc.FieldNo, RecBuf) then
Result := (Values[i] = nil) and Data.GetNull(FieldDesc.FieldNo, RecBuf)
else begin
Res := TMemData(Data).CompareFieldValue(Values[i], Types[i], FieldDesc, RecBuf, Options);
Result := (Res = 0) or (Res < 0) and (lxNearest in Options);
end;
Inc(i);
end;
if Result then
break;
end
else begin
Result := False;
break;
end;
end;
end;
if (SavePos or not Result)
and not (Data.Eof and Data.Bof {Empty Data}) then
Data.SetToBookmark(Bookmark);
finally
Marshal.FreeHGlobal(Bookmark);
for i := 0 to FieldCount - 1 do
Marshal.FreeHGlobal(Values[i]);
end;
end;
function TMemDataSet.LocateRecord(const KeyFields: string; const KeyValues: variant;
Options: TLocateExOptions; SavePos: boolean): boolean;
var
Fields: TDAList;
FieldDesc: TFieldDesc;
procedure ParseKeyFields;
var
St: string;
i: integer;
begin
i := 1;
while True do begin
St := ExtractFieldName(KeyFields, i);
if St <> '' then begin
FieldDesc := Data.FieldByName(St);
if FieldDesc <> nil then
Fields.Add(FieldDesc);
end
else
break;
end;
end;
begin
Fields := TDAList.Create;
try
ParseKeyFields;
Result := InternalLocateRecord(Fields, KeyValues, Options, SavePos);
finally
Fields.Free;
end;
end;
function TMemDataSet.LocateRecord(const KeyFields: array of TField; const KeyValues: variant;
Options: TLocateExOptions; SavePos: boolean): boolean;
var
Fields: TDAList;
i: integer;
begin
Fields := TDAList.Create;
try
for i := 0 to Length(KeyFields) - 1 do begin
if KeyFields[i] <> nil then
Fields.Add(GetFieldDesc(KeyFields[i]));
end;
Result := InternalLocateRecord(Fields, KeyValues, Options, SavePos);
finally
Fields.Free;
end;
end;
function LocateExOptions(Options: TLocateOptions): TLocateExOptions;
begin
Result := [];
if loCaseInsensitive in Options then
Result := Result + [lxCaseInsensitive];
if loPartialKey in Options then
Result := Result + [lxPartialKey];
end;
function TMemDataSet.Locate(const KeyFields: array of TField; const KeyValues: variant;
Options: TLocateOptions): boolean;
begin
DoBeforeScroll;
Result := LocateRecord(KeyFields, KeyValues, LocateExOptions(Options), False);
if Result then begin
Resync([{rmExact, rmCenter}]);
DoAfterScroll;
end;
end;
function TMemDataSet.Locate(const KeyFields: string;
const KeyValues: variant; Options: TLocateOptions): boolean;
begin
DoBeforeScroll;
Result := LocateRecord(KeyFields, KeyValues, LocateExOptions(Options), False);
if Result then begin
Resync([{rmExact, rmCenter}]);
DoAfterScroll;
end;
end;
function TMemDataSet.LocateEx(const KeyFields: string;
const KeyValues: variant; Options: TLocateExOptions): boolean;
begin
DoBeforeScroll;
Result := LocateRecord(KeyFields, KeyValues, Options, False);
if Result then begin
Resync([{rmExact, rmCenter}]);
DoAfterScroll;
end;
end;
function TMemDataSet.LocateEx(const KeyFields: array of TField;
const KeyValues: variant; Options: TLocateExOptions): boolean;
begin
DoBeforeScroll;
Result := LocateRecord(KeyFields, KeyValues, Options, False);
if Result then begin
Resync([{rmExact, rmCenter}]);
DoAfterScroll;
end;
end;
function TMemDataSet.Lookup(const KeyFields: string; const KeyValues: variant;
const ResultFields: string): variant;
begin
Result := Null;
if LocateRecord(KeyFields, KeyValues, [], True) then begin
SetTempState(dsCalcFields);
try
CalculateFields(TempBuffer);
Result := FieldValues[ResultFields];
finally
RestoreState(dsBrowse);
end;
end;
end;
{ CachedUpdates }
procedure TMemDataSet.CheckCachedUpdateMode;
begin
if not CachedUpdates then
DatabaseError(SNotCachedUpdate);
end;
function TMemDataSet.UpdateStatus: TUpdateStatus;
var
RecBuf: TRecordBuffer;
begin
if CachedUpdates and not IsEmpty then begin
if State = dsCalcFields then
RecBuf := CalcBuffer
else
RecBuf := ActiveBuffer;
Result := PRecInfo(integer(RecBuf) + FRecInfoOfs).UpdateStatus;
end
else
Result := usUnModified;
end;
function TMemDataSet.UpdateResult: TUpdateAction;
begin
UpdateCursorPos;
if Data.GetUpdateResult = urNone then
Result := uaApplied
else
Result := TUpdateAction(Data.GetUpdateResult);
end;
procedure TMemDataSet.ApplyUpdates;
begin
CheckActive;
FreeRefBuffers;
if State <> dsBrowse then
Post;
CheckCachedUpdateMode;
UpdateCursorPos;
NewCacheRecBuf := AllocRecordBuffer;
OldCacheRecBuf := AllocRecordBuffer;
FInCacheProcessing := True;
try
Data.SetCacheRecBuf(NewCacheRecBuf, OldCacheRecBuf);
Data.ApplyUpdates;
finally
FInCacheProcessing := False;
FreeRecordBuffer(NewCacheRecBuf);
FreeRecordBuffer(OldCacheRecBuf);
Resync([]);
end;
end;
procedure TMemDataSet.CommitUpdates;
begin
CheckActive;
CheckCachedUpdateMode;
FreeRefBuffers;
UpdateCursorPos;
NewCacheRecBuf := AllocRecordBuffer;
OldCacheRecBuf := AllocRecordBuffer;
FInCacheProcessing := True;
try
Data.SetCacheRecBuf(NewCacheRecBuf, OldCacheRecBuf);
Data.CommitUpdates;
finally
FInCacheProcessing := False;
FreeRecordBuffer(NewCacheRecBuf);
FreeRecordBuffer(OldCacheRecBuf);
end;
Resync([]);
end;
procedure TMemDataSet.CancelUpdates;
begin
CheckActive;
FreeRefBuffers;
Cancel;
CheckCachedUpdateMode;
UpdateCursorPos;
Data.CancelUpdates;
Resync([]);
end;
procedure TMemDataSet.RestoreUpdates;
begin
CheckActive;
FreeRefBuffers;
Cancel;
CheckCachedUpdateMode;
UpdateCursorPos;
Data.RestoreUpdates;
Resync([]);
end;
procedure TMemDataSet.RevertRecord;
begin
CheckActive;
FreeRefComplexFields(ActiveBuffer);
if State in dsEditModes then
Cancel;
CheckCachedUpdateMode;
UpdateCursorPos;
Data.RevertRecord;
Resync([]);
end;
procedure TMemDataSet.DoApplyRecord(UpdateKind: TUpdateRecKind; var Action: TUpdateRecAction; LastItem: boolean);
var
OldModified: boolean;
UpdateAction: TUpdateAction;
begin
OldModified := Modified; // NewValue change Modified ??? or MemDS
try
UpdateAction := uaFail;
try
if Assigned(OnUpdateRecord) then begin
OnUpdateRecord(Self, TUpdateKind(UpdateKind), UpdateAction);
if UpdateAction in [uaAbort] then
Abort;
end;
//else begin
if not Assigned(OnUpdateRecord) or (UpdateAction = TUpdateAction(uaDefault)) then begin
case UpdateKind of
ukUpdate:
PerformUpdate;
ukInsert:
PerformAppend;
ukDelete:
PerformDelete;
end;
if BatchUpdate then
if CanFlushBatch or LastItem then
// Should be flushed here because of Action parameter that should be returned
// to TMemData.ApplyUpdates function
FlushBatch
else begin
Action := urSuspended;
Exit;
end;
UpdateAction := uaApplied;
end;
except
on E: Exception do
if IsClass(E, EDatabaseError) and Assigned(OnUpdateError) then begin
OnUpdateError(Self, EDatabaseError(E), TUpdateKind(UpdateKind), UpdateAction);
case UpdateAction of
uaFail:
raise;
uaAbort:
Abort;
end;
end
else
raise;
end;
finally
SetModified(OldModified);
end;
Action := TUpdateRecAction(UpdateAction);
end;
function TMemDataSet.BatchUpdate: boolean;
begin
Result := False;
end;
function TMemDataSet.CanFlushBatch: boolean;
begin
Result := False;
end;
procedure TMemDataSet.FlushBatch;
begin
Assert(False, 'Should be overridden');
end;
{ BLOB Support }
{function TMemDataSet.GetBlobData(Field:TField; Buffer: PChar):TBlobData;
begin
Result := PBlobDataArray(Buffer + FBlobCacheOfs)[Field.Offset];
end;
procedure TMemDataSet.SetBlobData(Field:TField; Buffer: PChar; Value:TBlobData);
begin
if Buffer = ActiveBuffer then
PBlobDataArray(Buffer + FBlobCacheOfs)[Field.Offset] := Value;
end;
procedure TMemDataSet.ClearBlobCache(Buffer: PChar);
var
i: integer;
begin
for i := 0 to BlobFieldCount - 1 do
PBlobDataArray(Buffer + FBlobCacheOfs)[i] := '';
end;}
procedure TMemDataSet.CloseBlob(Field: TField);
begin
end;
function TMemDataSet.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
begin
Result := TBlobStream.Create(Field as TBlobField, Mode);
end;
{ Informational }
function TMemDataSet.IsSequenced: boolean;
begin
Result := True;
end;
function TMemDataSet.GetRecordSize: word;
begin
Result := word(Data.RecordSize);
end;
function TMemDataSet.GetRecordCount: integer;
begin
if Active then
Result := Data.RecordCount
else
Result := 0;
end;
function TMemDataSet.GetRecNo: integer;
var
RecBuf: TRecordBuffer;
begin
if GetActiveRecBuf(RecBuf) then
Result := PRecInfo(integer(RecBuf) + FRecInfoOfs).RecordNumber
else
Result := 0;
end;
procedure TMemDataSet.SetRecNo(Value: integer);
begin
CheckBrowseMode;
DoBeforeScroll;
Data.RecordNo := Value;
Resync([{rmCenter}]);
DoAfterScroll;
end;
{ More }
procedure TMemDataSet.InternalHandleException;
begin
{$IFDEF VER6P}
if Assigned(Classes.ApplicationHandleException) then
Classes.ApplicationHandleException(ExceptObject)
else
ShowException(ExceptObject, ExceptAddr)
{$ELSE}
ApplicationHandleException(Self);
{$ENDIF}
end;
{$IFDEF CLR}
procedure TMemDataSet.DataEvent(Event: TDataEvent; Info: TObject);
{$ELSE}
procedure TMemDataSet.DataEvent(Event: TDataEvent; Info: longint);
{$ENDIF}
procedure CheckIfParentScrolled;
var
ParentPosition, I: Integer;
begin
if FParentDataSet = nil then
Exit;
ParentPosition := 0;
with FParentDataSet do
if not IsEmpty then
for I := 0 to BookmarkSize - 1 do
ParentPosition := ParentPosition +
Marshal.ReadByte(ActiveBuffer, FBookmarkOfs + I);
if (FLastParentPos = 0) or (ParentPosition <> FLastParentPos) then
begin
First;
FLastParentPos := ParentPosition;
end else
begin
UpdateCursorPos;
Resync([]);
end;
end;
begin
if Event = deParentScroll then
CheckIfParentScrolled;
inherited DataEvent(Event, Info);
end;
procedure TMemDataSet.AssignTo(Dest: TPersistent);
begin
if Dest is TMemDataSet then begin
TMemDataSet(Dest).CachedUpdates := CachedUpdates;
TMemDataSet(Dest).LocalConstraints := LocalConstraints;
TMemDataSet(Dest).LocalUpdate := LocalUpdate;
end
else
inherited;
end;
procedure TMemDataSet.SetCachedUpdates(Value: boolean);
begin
if FCachedUpdates <> Value then begin
CheckInactive;
FCachedUpdates := Value;
Data.CachedUpdates := FCachedUpdates;
end;
end;
function TMemDataSet.GetUpdatesPending: boolean;
begin
Result := Data.UpdatesPending;
end;
function TMemDataSet.GetPrepared: boolean;
begin
Result := Data.Prepared;
end;
procedure TMemDataSet.SetPrepared(Value: boolean);
begin
if Value then
Prepare
else
UnPrepare;
end;
function TMemDataSet.GetUpdateRecordSet: TUpdateRecordTypes;
var
ItemTypes: TItemTypes;
begin
// if Active then begin
CheckCachedUpdateMode;
ItemTypes := Data.FilterItemTypes;
Result := [];
if isUnmodified in ItemTypes then
Result := Result + [rtUnmodified];
if isUpdated in ItemTypes then
Result := Result + [rtModified];
if isAppended in ItemTypes then
Result := Result + [rtInserted];
if isDeleted in ItemTypes then
Result := Result + [rtDeleted];
{ end
else
Result := [];}
end;
procedure TMemDataSet.SetUpdateRecordSet(Value: TUpdateRecordTypes);
var
ItemTypes: TItemTypes;
begin
CheckCachedUpdateMode;
//CheckBrowseMode;
if Active then
UpdateCursorPos;
ItemTypes := [];
if rtUnmodified in Value then
ItemTypes := ItemTypes + [isUnmodified];
if rtModified in Value then
ItemTypes := ItemTypes + [isUpdated];
if rtInserted in Value then
ItemTypes := ItemTypes + [isAppended];
if rtDeleted in Value then
ItemTypes := ItemTypes + [isDeleted];
Data.FilterItemTypes := ItemTypes;
if Active then
Resync([]);
end;
{ TBlobStream }
constructor TBlobStream.Create(Field: TBlobField; Mode: TBlobStreamMode);
begin
inherited Create;
FMode := Mode;
FField := Field;
FDataSet := FField.DataSet as TMemDataSet;
FFieldNo := FField.FieldNo;
if not FDataSet.GetActiveRecBuf(FBuffer) then
Exit;
{ if FDataSet.State = dsFilter then
DatabaseErrorFmt('SNoFieldAccess', [FField.DisplayName]);}
if not FField.Modified then begin
if Mode = bmRead then begin
{ FCached := FDataSet.FCacheBlobs and (FBuffer = FDataSet.ActiveBuffer) and
(FField.IsNull or (FDataSet.GetBlobData(FField, FBuffer) <> ''));}
end
else begin
// FDataSet.SetBlobData(FField, FBuffer, '');
if FField.ReadOnly then
DatabaseErrorFmt(SFieldReadOnly, [FField.DisplayName]);
if not (FDataSet.State in [dsNewValue, dsEdit, dsInsert]) then
DatabaseError(SNotEditing);
end;
end;
FOpened := True;
if Mode = bmWrite then
Truncate;
end;
destructor TBlobStream.Destroy;
begin
if FOpened then begin
if FModified then
FField.Modified := True;
end;
if FModified then
//try
{$IFDEF CLR}
FDataSet.DataEvent(deFieldChange, FField);
{$ELSE}
FDataSet.DataEvent(deFieldChange, longint(FField));
{$ENDIF}
{except
Application.HandleException(Self);
end;}
end;
{$IFDEF CLR}
function TBlobStream.Read(var Buffer: TBytes; Offset, Count: Longint): Longint;
var
Handle: IntPtr;
{$ELSE}
function TBlobStream.Read(var Buffer; Count: longint): longint;
{$ENDIF}
begin
Result := 0;
if FOpened then begin
if Count > Size - FPosition then
Result := Size - FPosition
else
Result := Count;
if Result > 0 then begin
{$IFDEF CLR}
Handle := AllocGCHandle(Buffer, True);
try
Result := FDataSet.Data.ReadBlob(FFieldNo, FBuffer, FPosition, Count,
GetAddrOfPinnedObject(Handle), FDataSet.State = dsOldValue{$IFDEF VER10P}, (FField is TWideMemoField){$ENDIF});
finally
FreeGCHandle(Handle);
end;
{$ELSE}
Result := FDataSet.Data.ReadBlob(FFieldNo, FBuffer, FPosition, Count, @Buffer,
(FDataSet.State = dsOldValue){$IFDEF VER10P}, (FField is TWideMemoField){$ENDIF});
{$ENDIF}
Inc(FPosition, Result);
end;
end;
end;
{$IFDEF CLR}
function TBlobStream.Write(const Buffer: TBytes; Offset, Count: Longint): Longint;
var
Handle: IntPtr;
{$ELSE}
function TBlobStream.Write(const Buffer; Count: Longint): Longint;
{$ENDIF}
begin
Result := 0;
if FOpened then begin
{$IFDEF CLR}
Handle := AllocGCHandle(Buffer, True);
try
FDataSet.Data.WriteBlob(FFieldNo, FBuffer, FPosition, Count,
GetAddrOfPinnedObject(Handle){$IFDEF VER10P}, FField is TWideMemoField{$ENDIF});
finally
FreeGCHandle(Handle);
end;
{$ELSE}
FDataSet.Data.WriteBlob(FFieldNo, FBuffer, FPosition, Count, @Buffer
{$IFDEF VER10P}, FField is TWideMemoField{$ENDIF});
{$ENDIF}
Inc(FPosition, Count);
Result := Count;
FModified := True;
{ FDataSet.SetBlobData(FField, FBuffer, '');}
end;
end;
{$IFDEF CLR}
function TBlobStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
{$ELSE}
function TBlobStream.Seek(Offset: Longint; Origin: Word): Longint;
{$ENDIF}
begin
case Origin of
soFromBeginning:
FPosition := Offset;
soFromCurrent:
Inc(FPosition, Offset);
soFromEnd:
FPosition := GetBlobSize + Offset;
end;
Result := FPosition;
end;
procedure TBlobStream.Truncate;
begin
if FOpened then begin
FDataSet.Data.TruncateBlob(FFieldNo, FBuffer, FPosition
{$IFDEF VER10P}, FField is TWideMemoField{$ENDIF});
FModified := True;
// FDataSet.SetBlobData(FField, FBuffer, '');
end;
end;
function TBlobStream.GetBlobSize: longint;
begin
Result := 0;
if FOpened then
Result := FDataSet.Data.GetBlobSize(FFieldNo, FBuffer, (FDataSet.State = dsOldValue)
{$IFDEF VER10P}, FField is TWideMemoField{$ENDIF});
end;
{$IFDEF CLR}
procedure TBlobStream.SetSize(NewSize: Int64);
{$ELSE}
procedure TBlobStream.SetSize(NewSize: Longint);
{$ENDIF}
begin
if FOpened then
FDataSet.Data.SetBlobSize(FFieldNo, FBuffer, NewSize, (FDataSet.State = dsOldValue)
{$IFDEF VER10P}, (FField is TWideMemoField){$ENDIF});
end;
class function TMemDSUtils.SetBlob(Obj: TMemDataSet; Field: TField; Blob: TBlob): boolean;
begin
Result := Obj.SetBlob(Field, Blob);
end;
class function TMemDSUtils.GetBlob(Obj: TMemDataSet; FieldDesc: TFieldDesc): TBlob;
begin
Result := Obj.InternalGetBlob(FieldDesc);
end;
end.