memds.pp 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 1999-2007 by the Free Pascal development team
  4. Some modifications (c) 2007 by Martin Schreiber
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$IFDEF FPC}
  12. {$mode objfpc}
  13. {$H+}
  14. {$ENDIF}
  15. {
  16. TMemDataset : In-memory dataset.
  17. - Has possibility to copy Structure/Data from other dataset.
  18. - Can load/save to/from stream.
  19. Ideas taken from THKMemTab Component by Harri Kasulke - Hamburg/Germany
  20. E-mail: [email protected]
  21. }
  22. unit memds;
  23. interface
  24. uses
  25. sysutils, classes, db, types;
  26. const
  27. // Stream Markers.
  28. MarkerSize = SizeOf(Integer);
  29. smEOF = 0;
  30. smFieldDefs = 1;
  31. smData = 2;
  32. type
  33. {$IFNDEF FPC}
  34. {$i memdsdelphi.inc} // should set ptrint is longint|intptr
  35. // & trecordbuffer ( if <2009)
  36. {$ENDIF}
  37. MDSError=class(Exception);
  38. { TMemDataset }
  39. TMemDataset=class(TDataSet)
  40. private
  41. type
  42. TMDSBlobList = class(TFPList)
  43. public
  44. procedure Clear; reintroduce;
  45. end;
  46. var
  47. FOpenStream : TStream;
  48. FFileName : String;
  49. FFileModified : Boolean;
  50. FStream: TMemoryStream;
  51. FRecInfoOffset: integer;
  52. FRecCount: integer;
  53. FRecSize: integer;
  54. FCurrRecNo: integer;
  55. FIsOpen: boolean;
  56. FTableIsCreated: boolean;
  57. FFilterBuffer: TRecordBuffer;
  58. ffieldoffsets: PInteger;
  59. ffieldsizes: PInteger;
  60. FBlobs: TMDSBlobList;
  61. function GetRecordBufferPointer(p:TRecordBuffer; Pos:Integer):TRecordBuffer;
  62. function GetIntegerPointer(p:PInteger; Pos:Integer):PInteger;
  63. procedure calcrecordlayout;
  64. function MDSGetRecordOffset(ARecNo: integer): longint;
  65. function MDSGetFieldOffset(FieldNo: integer): integer;
  66. function MDSGetBufferSize(FieldNo: integer): integer;
  67. function MDSGetActiveBuffer(out Buffer: TRecordBuffer): Boolean;
  68. procedure MDSReadRecord(Buffer:TRecordBuffer;ARecNo:Integer);
  69. procedure MDSWriteRecord(Buffer:TRecordBuffer;ARecNo:Integer);
  70. procedure MDSAppendRecord(Buffer:TRecordBuffer);
  71. function MDSFilterRecord(Buffer:TRecordBuffer): Boolean;
  72. function MDSLocateRecord(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions; out ARecNo: integer): Boolean;
  73. protected
  74. // Mandatory
  75. function AllocRecordBuffer: TRecordBuffer; override;
  76. procedure FreeRecordBuffer(var Buffer: TRecordBuffer); override;
  77. procedure GetBookmarkData(Buffer: TRecordBuffer; Data: Pointer); override;
  78. function GetBookmarkFlag(Buffer: TRecordBuffer): TBookmarkFlag; override;
  79. function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
  80. function GetRecord(Buffer: TRecordBuffer; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
  81. function GetRecordSize: Word; override;
  82. procedure InternalAddRecord(Buffer: Pointer; DoAppend: Boolean); override;
  83. procedure InternalClose; override;
  84. procedure InternalDelete; override;
  85. procedure InternalFirst; override;
  86. procedure InternalGotoBookmark(ABookmark: Pointer); override;
  87. procedure InternalInitFieldDefs; override;
  88. procedure InternalInitRecord(Buffer: TRecordBuffer); override;
  89. procedure ClearCalcFields(Buffer: TRecordBuffer); override;
  90. procedure InternalLast; override;
  91. procedure InternalOpen; override;
  92. procedure InternalPost; override;
  93. procedure InternalSetToRecord(Buffer: TRecordBuffer); override;
  94. function IsCursorOpen: Boolean; override;
  95. procedure SetBookmarkFlag(Buffer: TRecordBuffer; Value: TBookmarkFlag); override;
  96. procedure SetBookmarkData(Buffer: TRecordBuffer; Data: Pointer); override;
  97. procedure SetFieldData(Field: TField; Buffer: Pointer); override;
  98. // Optional.
  99. function GetRecordCount: Integer; override;
  100. procedure SetRecNo(Value: Integer); override;
  101. function GetRecNo: Integer; override;
  102. // Own.
  103. procedure SetFilterText(AValue: string); //silently drops filter
  104. Procedure RaiseError(Fmt : String; Args : Array of const);
  105. Procedure CheckMarker(F : TStream; Marker : Integer);
  106. Procedure WriteMarker(F : TStream; Marker : Integer);
  107. Procedure ReadFieldDefsFromStream(F : TStream);
  108. Procedure SaveFieldDefsToStream(F : TStream);
  109. // These should be overridden if you want to load more data.
  110. // E.g. index defs.
  111. Procedure LoadDataFromStream(F : TStream); virtual;
  112. // If SaveData=False, a size 0 block should be written.
  113. Procedure SaveDataToStream(F : TStream; SaveData : Boolean); virtual;
  114. public
  115. constructor Create(AOwner:TComponent); override;
  116. destructor Destroy; override;
  117. function BookmarkValid(ABookmark: TBookmark): Boolean; override;
  118. function CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Longint; override;
  119. function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; override;
  120. function Locate(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions): boolean; override;
  121. function Lookup(const KeyFields: string; const KeyValues: Variant; const ResultFields: string): Variant; override;
  122. procedure CreateTable;
  123. Function DataSize : Integer;
  124. Procedure Clear(ClearDefs : Boolean);{$IFNDEF FPC} overload; {$ENDIF}
  125. Procedure Clear;{$IFNDEF FPC} overload; {$ENDIF}
  126. Procedure SaveToFile(AFileName : String);{$IFNDEF FPC} overload; {$ENDIF}
  127. Procedure SaveToFile(AFileName : String; SaveData : Boolean);{$IFNDEF FPC} overload; {$ENDIF}
  128. Procedure SaveToStream(F : TStream); {$IFNDEF FPC} overload; {$ENDIF}
  129. Procedure SaveToStream(F : TStream; SaveData : Boolean);{$IFNDEF FPC} overload; {$ENDIF}
  130. Procedure LoadFromStream(F : TStream);
  131. Procedure LoadFromFile(AFileName : String);
  132. Procedure CopyFromDataset(DataSet : TDataSet); {$IFNDEF FPC} overload; {$ENDIF}
  133. Procedure CopyFromDataset(DataSet : TDataSet; CopyData : Boolean); {$IFNDEF FPC} overload; {$ENDIF}
  134. Property FileModified : Boolean Read FFileModified;
  135. // TMemDataset does not implement Filter. Please use OnFilter instead.
  136. Property Filter; unimplemented;
  137. published
  138. Property FileName : String Read FFileName Write FFileName;
  139. property Filtered;
  140. Property Active;
  141. Property FieldDefs;
  142. property BeforeOpen;
  143. property AfterOpen;
  144. property BeforeClose;
  145. property AfterClose;
  146. property BeforeInsert;
  147. property AfterInsert;
  148. property BeforeEdit;
  149. property AfterEdit;
  150. property BeforePost;
  151. property AfterPost;
  152. property BeforeCancel;
  153. property AfterCancel;
  154. property BeforeDelete;
  155. property AfterDelete;
  156. property BeforeScroll;
  157. property AfterScroll;
  158. property OnDeleteError;
  159. property OnEditError;
  160. property OnNewRecord;
  161. property OnPostError;
  162. property OnFilterRecord;
  163. end;
  164. implementation
  165. uses
  166. DBConst, Variants, FmtBCD;
  167. ResourceString
  168. SErrFieldTypeNotSupported = 'Fieldtype of Field "%s" not supported.';
  169. SErrBookMarkNotFound = 'Bookmark %d not found.';
  170. SErrInvalidDataStream = 'Error in data stream at position %d';
  171. SErrInvalidMarkerAtPos = 'Wrong data stream marker at position %d. Got %d, expected %d';
  172. SErrNoFileName = 'Filename must not be empty.';
  173. type
  174. TMDSRecInfo=record
  175. Bookmark: Longint;
  176. BookmarkFlag: TBookmarkFlag;
  177. end;
  178. PRecInfo=^TMDSRecInfo;
  179. TMDSBlobField = record
  180. Buffer: Pointer; // pointer to memory allocated for Blob data
  181. Size: PtrInt; // size of Blob data
  182. end;
  183. { TMDSBlobStream }
  184. TMDSBlobStream = class(TStream)
  185. private
  186. FField : TBlobField;
  187. FDataSet : TMemDataset;
  188. FBlobField : TMDSBlobField;
  189. FPosition : PtrInt;
  190. FModified : boolean;
  191. procedure AllocBlobField(NewSize: PtrInt);
  192. procedure FreeBlobField;
  193. public
  194. constructor Create(Field: TField; Mode: TBlobStreamMode);
  195. destructor Destroy; override;
  196. function Seek(const Offset: int64; Origin: TSeekOrigin): int64; override;
  197. function Read(var Buffer; Count: Longint): Longint; override;
  198. function Write(const Buffer; Count: Longint): Longint; override;
  199. end;
  200. Const
  201. SizeRecInfo = SizeOf(TMDSRecInfo);
  202. procedure unsetfieldisnull(nullmask: pbyte; const x: integer);
  203. begin
  204. inc(nullmask,(x shr 3));
  205. nullmask^:= nullmask^ or (1 shl (x and 7));
  206. end;
  207. procedure setfieldisnull(nullmask: pbyte; const x: integer);
  208. begin
  209. inc(nullmask,(x shr 3));
  210. nullmask^:= nullmask^ and Not (1 shl (x and 7));
  211. end;
  212. function getfieldisnull(nullmask: pbyte; const x: integer): boolean;
  213. begin
  214. inc(nullmask,(x shr 3));
  215. result:= nullmask^ and (1 shl (x and 7)) = 0;
  216. end;
  217. { ---------------------------------------------------------------------
  218. Stream functions
  219. ---------------------------------------------------------------------}
  220. Function ReadInteger(S : TStream) : Integer;
  221. begin
  222. S.ReadBuffer(Result,SizeOf(Result));
  223. end;
  224. Function ReadString(S : TStream) : String;
  225. Var
  226. L : Integer;
  227. begin
  228. L:=ReadInteger(S);
  229. Setlength(Result,L);
  230. If (L<>0) then
  231. S.ReadBuffer(Result[1],L);
  232. end;
  233. Procedure WriteInteger(S : TStream; Value : Integer);
  234. begin
  235. S.WriteBuffer(Value,SizeOf(Value));
  236. end;
  237. Procedure WriteString(S : TStream; Value : String);
  238. Var
  239. L : Integer;
  240. begin
  241. L:=Length(Value);
  242. WriteInteger(S,Length(Value));
  243. If (L<>0) then
  244. S.WriteBuffer(Value[1],L);
  245. end;
  246. { TMDSBlobStream }
  247. constructor TMDSBlobStream.Create(Field: TField; Mode: TBlobStreamMode);
  248. begin
  249. FField := Field as TBlobField;
  250. FDataSet := Field.DataSet as TMemDataset;
  251. if not Field.GetData(@FBlobField) then // IsNull
  252. begin
  253. FBlobField.Buffer := nil;
  254. FBlobField.Size := 0;
  255. end;
  256. if Mode = bmWrite then
  257. // release existing Blob
  258. FreeBlobField;
  259. end;
  260. destructor TMDSBlobStream.Destroy;
  261. begin
  262. if FModified then
  263. begin
  264. if FBlobField.Size = 0 then // Empty blob = IsNull
  265. FField.SetData(nil)
  266. else
  267. FField.SetData(@FBlobField);
  268. end;
  269. inherited;
  270. end;
  271. procedure TMDSBlobStream.FreeBlobField;
  272. begin
  273. FDataSet.FBlobs.Remove(FBlobField.Buffer);
  274. FreeMem(FBlobField.Buffer, FBlobField.Size);
  275. FBlobField.Buffer := nil;
  276. FBlobField.Size := 0;
  277. FModified := True;
  278. end;
  279. procedure TMDSBlobStream.AllocBlobField(NewSize: PtrInt);
  280. begin
  281. FDataSet.FBlobs.Remove(FBlobField.Buffer);
  282. ReAllocMem(FBlobField.Buffer, NewSize);
  283. FDataSet.FBlobs.Add(FBlobField.Buffer);
  284. FModified := True;
  285. end;
  286. function TMDSBlobStream.Seek(const Offset: int64; Origin: TSeekOrigin): int64;
  287. begin
  288. Case Origin of
  289. soBeginning : FPosition := Offset;
  290. soEnd : FPosition := FBlobField.Size + Offset;
  291. soCurrent : FPosition := FPosition + Offset;
  292. end;
  293. Result := FPosition;
  294. end;
  295. function TMDSBlobStream.Read(var Buffer; Count: Longint): Longint;
  296. var p: Pointer;
  297. begin
  298. if FPosition + Count > FBlobField.Size then
  299. Count := FBlobField.Size - FPosition;
  300. p := FBlobField.Buffer + FPosition;
  301. Move(p^, Buffer, Count);
  302. Inc(FPosition, Count);
  303. Result := Count;
  304. end;
  305. function TMDSBlobStream.Write(const Buffer; Count: Longint): Longint;
  306. var p: Pointer;
  307. begin
  308. AllocBlobField(FPosition+Count);
  309. p := FBlobField.Buffer + FPosition;
  310. Move(Buffer, p^, Count);
  311. Inc(FBlobField.Size, Count);
  312. Inc(FPosition, Count);
  313. Result := Count;
  314. end;
  315. { TMemDataset.TMDSBlobList }
  316. procedure TMemDataset.TMDSBlobList.Clear;
  317. var i: integer;
  318. begin
  319. for i:=0 to Count-1 do FreeMem(Items[i]);
  320. inherited Clear;
  321. end;
  322. { ---------------------------------------------------------------------
  323. TMemDataset
  324. ---------------------------------------------------------------------}
  325. constructor TMemDataset.Create(AOwner:TComponent);
  326. begin
  327. inherited Create(AOwner);
  328. FStream:=TMemoryStream.Create;
  329. FRecCount:=0;
  330. FRecSize:=0;
  331. FRecInfoOffset:=0;
  332. FCurrRecNo:=-1;
  333. BookmarkSize := sizeof(Longint);
  334. FBlobs := TMDSBlobList.Create;
  335. end;
  336. destructor TMemDataset.Destroy;
  337. begin
  338. // FStream.Free;
  339. FreeMem(FFieldOffsets);
  340. FreeMem(FFieldSizes);
  341. FBlobs.Clear;
  342. FBlobs.Free;
  343. inherited Destroy;
  344. FStream.Free;
  345. end;
  346. function TMemDataset.BookmarkValid(ABookmark: TBookmark): Boolean;
  347. var
  348. ReqBookmark: integer;
  349. begin
  350. Result := False;
  351. if ABookMark=nil then exit;
  352. ReqBookmark:=PInteger(ABookmark)^;
  353. Result := (ReqBookmark>=0) and (ReqBookmark<FRecCount);
  354. end;
  355. function TMemDataset.CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Longint;
  356. const r: array[Boolean, Boolean] of ShortInt = ((2,-1),(1,0));
  357. begin
  358. Result := r[Bookmark1=nil, Bookmark2=nil];
  359. if Result = 2 then
  360. Result := PInteger(Bookmark1)^ - PInteger(Bookmark2)^;
  361. end;
  362. function TMemDataset.CreateBlobStream(Field: TField; Mode: TBlobStreamMode
  363. ): TStream;
  364. begin
  365. // Blobs are not saved to stream/file !
  366. if Mode = bmWrite then
  367. begin
  368. if not (State in [dsEdit, dsInsert, dsFilter, dsCalcFields]) then
  369. DatabaseErrorFmt(SNotEditing, [Name], Self);
  370. if Field.ReadOnly and not (State in [dsSetKey, dsFilter]) then
  371. DatabaseErrorFmt(SReadOnlyField, [Field.DisplayName]);
  372. end;
  373. Result := TMDSBlobStream.Create(Field, Mode);
  374. end;
  375. function TMemDataset.MDSGetRecordOffset(ARecNo: integer): longint;
  376. begin
  377. Result:=FRecSize*ARecNo
  378. end;
  379. function TMemDataset.MDSGetFieldOffset(FieldNo: integer): integer;
  380. begin
  381. Result:= getIntegerPointer(ffieldoffsets, fieldno-1)^;
  382. end;
  383. procedure TMemDataset.RaiseError(Fmt: String; Args: array of const);
  384. begin
  385. Raise MDSError.CreateFmt(Fmt,Args);
  386. end;
  387. function TMemDataset.MDSGetBufferSize(FieldNo: integer): integer;
  388. var
  389. FD: TFieldDef;
  390. begin
  391. FD := FieldDefs.Items[FieldNo-1];
  392. case FD.DataType of
  393. ftString : Result:=FD.Size*FD.CharSize+1;
  394. ftGuid: result:=FD.Size+1;
  395. ftFixedChar:result:=FD.Size*FD.CharSize+1;
  396. ftBoolean: result:=SizeOf(Wordbool);
  397. ftCurrency,
  398. ftFloat: result:=SizeOf(Double);
  399. ftBCD: result:=SizeOf(currency);
  400. ftLargeInt: result:=SizeOf(int64);
  401. ftSmallInt: result:=SizeOf(SmallInt);
  402. ftWord,
  403. ftAutoInc,
  404. ftInteger: result:=SizeOf(longint);
  405. ftDateTime,
  406. ftTime,
  407. ftDate: result:=SizeOf(TDateTime);
  408. ftFmtBCD: result:=SizeOf(TBCD);
  409. ftWideString, ftFixedWideChar:
  410. result:=(FD.Size+1)*SizeOf(WideChar);
  411. ftBytes: result := FD.Size;
  412. ftVarBytes: result := FD.Size + SizeOf(Word);
  413. ftBlob, ftMemo, ftWideMemo:
  414. result := SizeOf(TMDSBlobField);
  415. ftLongWord: Result := SizeOf(LongWord);
  416. ftShortInt: Result := SizeOf(ShortInt);
  417. ftByte: Result := SizeOf(Byte);
  418. ftExtended: Result := SizeOf(Extended);
  419. ftSingle : Result := SizeOf(Single);
  420. else
  421. RaiseError(SErrFieldTypeNotSupported,[FD.Name]);
  422. end;
  423. {$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
  424. Result:=Align(Result,4);
  425. {$ENDIF}
  426. end;
  427. function TMemDataset.MDSGetActiveBuffer(out Buffer: TRecordBuffer): Boolean;
  428. begin
  429. case State of
  430. dsEdit,
  431. dsInsert:
  432. Buffer:=ActiveBuffer;
  433. dsFilter:
  434. Buffer:=FFilterBuffer;
  435. dsCalcFields:
  436. Buffer:=CalcBuffer;
  437. else
  438. if IsEmpty then
  439. Buffer:=nil
  440. else
  441. Buffer:=ActiveBuffer;
  442. end;
  443. Result := Buffer<>nil;
  444. end;
  445. procedure TMemDataset.MDSReadRecord(Buffer:TRecordBuffer;ARecNo:Integer); //Reads a Rec from Stream in Buffer
  446. begin
  447. FStream.Position:=MDSGetRecordOffset(ARecNo);
  448. FStream.ReadBuffer(Buffer^, FRecSize);
  449. end;
  450. procedure TMemDataset.MDSWriteRecord(Buffer:TRecordBuffer;ARecNo:Integer); //Writes a Rec from Buffer to Stream
  451. begin
  452. FStream.Position:=MDSGetRecordOffset(ARecNo);
  453. FStream.WriteBuffer(Buffer^, FRecSize);
  454. FFileModified:=True;
  455. end;
  456. procedure TMemDataset.MDSAppendRecord(Buffer:TRecordBuffer); //Appends a Rec (from Buffer) to Stream
  457. begin
  458. FStream.Position:=MDSGetRecordOffset(FRecCount);
  459. FStream.WriteBuffer(Buffer^, FRecSize);
  460. FFileModified:=True;
  461. end;
  462. //Abstract Overrides
  463. function TMemDataset.AllocRecordBuffer: TRecordBuffer;
  464. begin
  465. GetMem(Result, FRecSize+CalcFieldsSize);
  466. end;
  467. procedure TMemDataset.FreeRecordBuffer (var Buffer: TRecordBuffer);
  468. begin
  469. FreeMem(Buffer);
  470. end;
  471. procedure TMemDataset.InternalInitRecord(Buffer: TRecordBuffer);
  472. begin
  473. FillChar(Buffer^,FRecSize,0);
  474. end;
  475. procedure TMemDataset.ClearCalcFields(Buffer: TRecordBuffer);
  476. begin
  477. FillChar(Buffer[RecordSize], CalcFieldsSize, 0);
  478. end;
  479. procedure TMemDataset.InternalDelete;
  480. Var
  481. TS : TMemoryStream;
  482. begin
  483. if (FCurrRecNo<0) or (FCurrRecNo>=FRecCount) then
  484. Exit;
  485. // Very inefficient. We should simply move the last part closer to the beginning in
  486. // The FStream.
  487. TS:=TMemoryStream.Create;
  488. Try
  489. if FCurrRecNo>0 then
  490. begin
  491. FStream.Position:=MDSGetRecordOffset(0); //Delete Rec
  492. if FCurrRecNo<FRecCount-1 then
  493. begin
  494. TS.CopyFrom(FStream, MDSGetRecordOffset(FCurrRecNo)-MDSGetRecordOffset(0));
  495. FStream.Position:=MDSGetRecordOffset(FCurrRecNo+1);
  496. TS.CopyFrom(FStream,(MDSGetRecordOffset(FRecCount))-MDSGetRecordOffset(FCurrRecNo+1));
  497. end
  498. else
  499. TS.CopyFrom(FStream,MDSGetRecordOffset(FRecCount-1));
  500. end
  501. else
  502. begin //Delete first Rec
  503. FStream.Position:=MDSGetRecordOffset(FCurrRecNo+1);
  504. TS.CopyFrom(FStream,(MDSGetRecordOffset(FRecCount))-MDSGetRecordOffset(FCurrRecNo+1));
  505. end;
  506. FStream.LoadFromStream(TS);
  507. Dec(FRecCount);
  508. if FRecCount=0 then
  509. FCurrRecNo:=-1
  510. else
  511. if FCurrRecNo>=FRecCount then FCurrRecNo:=FRecCount-1;
  512. Finally
  513. TS.Free;
  514. end;
  515. FFileModified:=True;
  516. end;
  517. procedure TMemDataset.InternalInitFieldDefs;
  518. begin
  519. If (FOpenStream<>Nil) then
  520. ReadFieldDefsFromStream(FOpenStream);
  521. end;
  522. procedure TMemDataset.CheckMarker(F: TStream; Marker: Integer);
  523. Var
  524. I,P : Integer;
  525. begin
  526. P:=F.Position;
  527. If F.Read(I,MarkerSize)<>MarkerSize then
  528. RaiseError(SErrInvalidDataStream,[P])
  529. else
  530. if (I<>Marker) then
  531. RaiseError(SErrInvalidMarkerAtPos,[P,I,Marker]);
  532. end;
  533. procedure TMemDataset.ReadFieldDefsFromStream(F : TStream);
  534. Var
  535. I,ACount : Integer;
  536. FN : String;
  537. FS : Integer;
  538. B : Boolean;
  539. FT : TFieldType;
  540. begin
  541. CheckMarker(F,smFieldDefs);
  542. FieldDefs.Clear;
  543. ACount:=ReadInteger(F);
  544. For I:=1 to ACount do
  545. begin
  546. FN:=ReadString(F);
  547. FS:=ReadInteger(F);
  548. FT:=TFieldType(ReadInteger(F));
  549. B:=ReadInteger(F)<>0;
  550. TFieldDef.Create(FieldDefs,FN,ft,FS,B,I);
  551. end;
  552. FTableIsCreated:=False;
  553. end;
  554. procedure TMemDataset.InternalFirst;
  555. begin
  556. FCurrRecNo:=-1;
  557. end;
  558. procedure TMemDataset.InternalLast;
  559. begin
  560. FCurrRecNo:=FRecCount;
  561. end;
  562. procedure TMemDataset.InternalOpen;
  563. begin
  564. If (FFileName<>'') and FileExists(FFileName) then
  565. FOpenStream:=TFileStream.Create(FFileName,fmOpenRead);
  566. Try
  567. InternalInitFieldDefs;
  568. if DefaultFields then
  569. CreateFields;
  570. BindFields(True); // BindFields computes CalcFieldsSize
  571. if not FTableIsCreated then
  572. CreateTable;
  573. FCurrRecNo:=-1;
  574. If (FOpenStream<>Nil) then
  575. begin
  576. LoadDataFromStream(FOpenStream);
  577. CheckMarker(FOpenStream,smEOF);
  578. end;
  579. Finally
  580. FreeAndNil(FOpenStream);
  581. end;
  582. FIsOpen:=True;
  583. end;
  584. procedure TMemDataset.LoadDataFromStream(F: TStream);
  585. Var
  586. Size : Integer;
  587. begin
  588. CheckMarker(F,smData);
  589. Size:=ReadInteger(F);
  590. FBlobs.Clear;
  591. FStream.Clear;
  592. FStream.CopyFrom(F,Size);
  593. FRecCount:=Size div FRecSize;
  594. FCurrRecNo:=-1;
  595. end;
  596. procedure TMemDataset.LoadFromStream(F: TStream);
  597. begin
  598. Close;
  599. ReadFieldDefsFromStream(F);
  600. CreateTable;
  601. LoadDataFromStream(F);
  602. CheckMarker(F,smEOF);
  603. FFileModified:=False;
  604. end;
  605. procedure TMemDataset.LoadFromFile(AFileName: String);
  606. Var
  607. F : TFileStream;
  608. begin
  609. F:=TFileStream.Create(AFileName,fmOpenRead);
  610. Try
  611. LoadFromStream(F);
  612. Finally
  613. F.Free;
  614. end;
  615. end;
  616. procedure TMemDataset.SaveToFile(AFileName: String);
  617. begin
  618. SaveToFile(AFileName,True);
  619. end;
  620. procedure TMemDataset.SaveToFile(AFileName: String; SaveData: Boolean);
  621. Var
  622. F : TFileStream;
  623. begin
  624. If (AFileName='') then
  625. RaiseError(SErrNoFileName,[]);
  626. F:=TFileStream.Create(AFileName,fmCreate);
  627. try
  628. SaveToStream(F,SaveData);
  629. Finally
  630. F.Free;
  631. end;
  632. end;
  633. procedure TMemDataset.WriteMarker(F: TStream; Marker: Integer);
  634. begin
  635. Writeinteger(F,Marker);
  636. end;
  637. procedure TMemDataset.SaveToStream(F: TStream);
  638. begin
  639. SaveToStream(F,True);
  640. end;
  641. procedure TMemDataset.SaveToStream(F: TStream; SaveData: Boolean);
  642. begin
  643. SaveFieldDefsToStream(F);
  644. If SaveData then
  645. SaveDataToStream(F,SaveData);
  646. WriteMarker(F,smEOF);
  647. end;
  648. procedure TMemDataset.SaveFieldDefsToStream(F: TStream);
  649. Var
  650. I : Integer;
  651. FD : TFieldDef;
  652. begin
  653. WriteMarker(F,smFieldDefs);
  654. WriteInteger(F,FieldDefs.Count);
  655. For I:=1 to FieldDefs.Count do
  656. begin
  657. FD:=FieldDefs[I-1];
  658. WriteString(F,FD.Name);
  659. WriteInteger(F,FD.Size);
  660. WriteInteger(F,Ord(FD.DataType));
  661. WriteInteger(F,Ord(FD.Required));
  662. end;
  663. end;
  664. procedure TMemDataset.SaveDataToStream(F: TStream; SaveData: Boolean);
  665. begin
  666. if SaveData then
  667. begin
  668. WriteMarker(F,smData);
  669. WriteInteger(F,FStream.Size);
  670. FStream.Position:=0;
  671. F.CopyFrom(FStream,FStream.Size);
  672. FFileModified:=False;
  673. end
  674. else
  675. begin
  676. WriteMarker(F,smData);
  677. WriteInteger(F,0);
  678. end;
  679. end;
  680. procedure TMemDataset.InternalClose;
  681. begin
  682. if (FFileModified) and (FFileName<>'') then begin
  683. SaveToFile(FFileName,True);
  684. end;
  685. FIsOpen:=False;
  686. FFileModified:=False;
  687. // BindFields(False);
  688. if DefaultFields then
  689. DestroyFields;
  690. end;
  691. procedure TMemDataset.InternalPost;
  692. begin
  693. CheckActive;
  694. if not (State in [dsEdit, dsInsert]) then
  695. Exit;
  696. inherited InternalPost;
  697. if (State=dsEdit) then
  698. MDSWriteRecord(ActiveBuffer, FCurrRecNo)
  699. else
  700. InternalAddRecord(ActiveBuffer,True);
  701. end;
  702. function TMemDataset.IsCursorOpen: Boolean;
  703. begin
  704. Result:=FIsOpen;
  705. end;
  706. function TMemDataset.GetRecord(Buffer: TRecordBuffer; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
  707. var
  708. Accepted: Boolean;
  709. begin
  710. Result:=grOk;
  711. Accepted:=False;
  712. if (FRecCount<1) then
  713. begin
  714. Result:=grEOF;
  715. exit;
  716. end;
  717. repeat
  718. case GetMode of
  719. gmCurrent:
  720. if (FCurrRecNo>=FRecCount) or (FCurrRecNo<0) then
  721. Result:=grError;
  722. gmNext:
  723. if (FCurrRecNo<FRecCount-1) then
  724. Inc(FCurrRecNo)
  725. else
  726. Result:=grEOF;
  727. gmPrior:
  728. if (FCurrRecNo>0) then
  729. Dec(FCurrRecNo)
  730. else
  731. result:=grBOF;
  732. end;
  733. if result=grOK then
  734. begin
  735. MDSReadRecord(Buffer, FCurrRecNo);
  736. PRecInfo(Buffer+FRecInfoOffset)^.Bookmark:=FCurrRecNo;
  737. PRecInfo(Buffer+FRecInfoOffset)^.BookmarkFlag:=bfCurrent;
  738. GetCalcFields(Buffer);
  739. if (Filtered) then
  740. Accepted:=MDSFilterRecord(Buffer) //Filtering
  741. else
  742. Accepted:=True;
  743. if (GetMode=gmCurrent) and not Accepted then
  744. result:=grError;
  745. end;
  746. until (result<>grOK) or Accepted;
  747. end;
  748. function TMemDataset.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
  749. var
  750. SrcBuffer: TRecordBuffer;
  751. I: integer;
  752. begin
  753. I:= Field.FieldNo - 1;
  754. result := MDSGetActiveBuffer(SrcBuffer);
  755. if not result then Exit;
  756. if I >= 0 then
  757. begin
  758. result := not getfieldisnull(pointer(srcbuffer),I);
  759. if result and assigned(Buffer) then
  760. Move(GetRecordBufferPointer(SrcBuffer, GetIntegerPointer(ffieldoffsets,I)^)^, Buffer^, GetIntegerPointer(FFieldSizes, I)^);
  761. end
  762. else // Calculated, Lookup
  763. begin
  764. Inc(SrcBuffer, RecordSize + Field.Offset);
  765. result := Boolean(SrcBuffer[0]);
  766. if result and assigned(Buffer) then
  767. Move(SrcBuffer[1], Buffer^, Field.DataSize);
  768. end;
  769. end;
  770. procedure TMemDataset.SetFieldData(Field: TField; Buffer: Pointer);
  771. var
  772. DestBuffer: TRecordBuffer;
  773. I,J: integer;
  774. begin
  775. I:= Field.FieldNo - 1;
  776. if not MDSGetActiveBuffer(DestBuffer) then Exit;
  777. if I >= 0 then
  778. begin
  779. if State in [dsEdit, dsInsert, dsNewValue] then
  780. Field.Validate(Buffer);
  781. if Buffer = nil then
  782. setfieldisnull(pointer(DestBuffer),I)
  783. else
  784. begin
  785. unsetfieldisnull(pointer(DestBuffer),I);
  786. J:=GetIntegerPointer(FFieldSizes, I)^;
  787. if Field.DataType=ftString then
  788. Dec(J); // Do not move terminating 0, which is in the size.
  789. Move(Buffer^, GetRecordBufferPointer(DestBuffer, getIntegerPointer(FFieldOffsets, I)^)^, J);
  790. end;
  791. end
  792. else // Calculated, Lookup
  793. begin
  794. Inc(DestBuffer, RecordSize + Field.Offset);
  795. Boolean(DestBuffer[0]) := Buffer <> nil;
  796. if assigned(Buffer) then
  797. Move(Buffer^, DestBuffer[1], Field.DataSize);
  798. end;
  799. if not (State in [dsCalcFields, dsFilter, dsNewValue]) then
  800. DataEvent(deFieldChange, PtrInt(Field));
  801. end;
  802. function TMemDataset.GetRecordSize: Word;
  803. begin
  804. Result:= FRecSize;
  805. end;
  806. procedure TMemDataset.InternalGotoBookmark(ABookmark: Pointer);
  807. var
  808. ReqBookmark: integer;
  809. begin
  810. ReqBookmark:=PInteger(ABookmark)^;
  811. if (ReqBookmark>=0) and (ReqBookmark<FRecCount) then
  812. FCurrRecNo:=ReqBookmark
  813. else
  814. RaiseError(SErrBookMarkNotFound,[ReqBookmark]);
  815. end;
  816. procedure TMemDataset.InternalSetToRecord(Buffer: TRecordBuffer);
  817. var
  818. ReqBookmark: integer;
  819. begin
  820. ReqBookmark:=PRecInfo(Buffer+FRecInfoOffset)^.Bookmark;
  821. InternalGotoBookmark (@ReqBookmark);
  822. end;
  823. function TMemDataset.GetBookmarkFlag(Buffer: TRecordBuffer): TBookmarkFlag;
  824. begin
  825. Result:=PRecInfo(Buffer+FRecInfoOffset)^.BookmarkFlag;
  826. end;
  827. procedure TMemDataset.SetBookmarkFlag(Buffer: TRecordBuffer; Value: TBookmarkFlag);
  828. begin
  829. PRecInfo(Buffer+FRecInfoOffset)^.BookmarkFlag := Value;
  830. end;
  831. procedure TMemDataset.GetBookmarkData(Buffer: TRecordBuffer; Data: Pointer);
  832. begin
  833. if Data<>nil then
  834. PInteger(Data)^:=PRecInfo(Buffer+FRecInfoOffset)^.Bookmark;
  835. end;
  836. procedure TMemDataset.SetBookmarkData(Buffer: TRecordBuffer; Data: Pointer);
  837. begin
  838. if Data<>nil then
  839. PRecInfo(Buffer+FRecInfoOffset)^.Bookmark:=PInteger(Data)^
  840. else
  841. PRecInfo(Buffer+FRecInfoOffset)^.Bookmark:=0;
  842. end;
  843. function TMemDataset.MDSFilterRecord(Buffer: TRecordBuffer): Boolean;
  844. var
  845. SaveState: TDatasetState;
  846. begin
  847. Result:=True;
  848. if not Assigned(OnFilterRecord) then
  849. Exit;
  850. SaveState:=SetTempState(dsFilter);
  851. Try
  852. FFilterBuffer:=Buffer;
  853. OnFilterRecord(Self,Result);
  854. Finally
  855. RestoreState(SaveState);
  856. end;
  857. end;
  858. function TMemDataset.DataSize: Integer;
  859. begin
  860. Result:=FStream.Size;
  861. end;
  862. procedure TMemDataset.Clear;
  863. begin
  864. Clear(True);
  865. end;
  866. procedure TMemDataset.Clear(ClearDefs : Boolean);
  867. begin
  868. FBlobs.Clear;
  869. FStream.Clear;
  870. FRecCount:=0;
  871. FCurrRecNo:=-1;
  872. if Active then
  873. Resync([]);
  874. If ClearDefs then
  875. begin
  876. Close;
  877. FieldDefs.Clear;
  878. FTableIsCreated:=False;
  879. end;
  880. end;
  881. procedure TMemDataset.calcrecordlayout;
  882. var
  883. i,Count,aSize : integer;
  884. begin
  885. Count := FieldDefs.Count;
  886. // Avoid mem-leak if CreateTable is called twice
  887. FreeMem(FFieldOffsets);
  888. Freemem(FFieldSizes);
  889. {$IFDEF FPC}
  890. FFieldOffsets:=getmem(Count*sizeof(integer));
  891. FFieldSizes:=getmem(Count*sizeof(integer));
  892. {$ELSE}
  893. getmem(FFieldOffsets, Count*sizeof(integer));
  894. getmem(FFieldSizes, Count*sizeof(integer));
  895. {$ENDIF}
  896. FRecSize:= (Count+7) div 8; //null mask
  897. {$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
  898. FRecSize:=Align(FRecSize,4);
  899. {$ENDIF}
  900. for i:= 0 to Count-1 do
  901. begin
  902. GetIntegerPointer(FFieldOffsets, i)^ := FRecSize;
  903. aSize:=MDSGetBufferSize(i+1);
  904. GetIntegerPointer(FFieldSizes, i)^ := aSize;
  905. FRecSize:= FRecSize+aSize;
  906. end;
  907. FRecInfoOffset:=FRecSize;
  908. FRecSize:=FRecSize+SizeRecInfo;
  909. end;
  910. procedure TMemDataset.CreateTable;
  911. begin
  912. CheckInactive;
  913. Clear(False);
  914. calcrecordlayout;
  915. FTableIsCreated:=True;
  916. end;
  917. procedure TMemDataset.InternalAddRecord(Buffer: Pointer; DoAppend: Boolean);
  918. begin
  919. MDSAppendRecord(ActiveBuffer);
  920. InternalLast;
  921. Inc(FRecCount);
  922. end;
  923. procedure TMemDataset.SetRecNo(Value: Integer);
  924. begin
  925. CheckBrowseMode;
  926. if (Value>=1) and (Value<=FRecCount) then
  927. begin
  928. FCurrRecNo:=Value-1;
  929. Resync([]);
  930. DoAfterScroll;
  931. end;
  932. end;
  933. function TMemDataset.GetRecNo: Integer;
  934. begin
  935. UpdateCursorPos;
  936. if (FCurrRecNo<0) or (FRecCount=0) or (State=dsInsert) then
  937. Result:=0
  938. else
  939. Result:=FCurrRecNo+1;
  940. end;
  941. function TMemDataset.GetRecordCount: Integer;
  942. begin
  943. CheckActive;
  944. Result:=FRecCount;
  945. end;
  946. procedure TMemDataset.CopyFromDataset(DataSet: TDataSet);
  947. begin
  948. CopyFromDataset(Dataset,True);
  949. end;
  950. procedure TMemDataset.CopyFromDataset(DataSet: TDataSet; CopyData: Boolean);
  951. Var
  952. I : Integer;
  953. F,F1,F2 : TField;
  954. L1,L2 : TList;
  955. N : String;
  956. OriginalPosition: TBookMark;
  957. begin
  958. Clear(True);
  959. // NOT from FieldDefs. The data may not be available in buffers !!
  960. For I:=0 to Dataset.FieldCount-1 do
  961. begin
  962. F:=Dataset.Fields[I];
  963. TFieldDef.Create(FieldDefs,F.FieldName,F.DataType,F.Size,F.Required,F.FieldNo);
  964. end;
  965. CreateTable;
  966. If CopyData then
  967. begin
  968. Open;
  969. L1:=TList.Create;
  970. Try
  971. L2:=TList.Create;
  972. Try
  973. For I:=0 to FieldDefs.Count-1 do
  974. begin
  975. N:=FieldDefs[I].Name;
  976. F1:=FieldByName(N);
  977. F2:=DataSet.FieldByName(N);
  978. L1.Add(F1);
  979. L2.Add(F2);
  980. end;
  981. DisableControls;
  982. Dataset.DisableControls;
  983. OriginalPosition:=Dataset.GetBookmark;
  984. Try
  985. Dataset.Open;
  986. Dataset.First; //make sure we copy from the beginning
  987. While not Dataset.EOF do
  988. begin
  989. Append;
  990. For I:=0 to L1.Count-1 do
  991. begin
  992. F1:=TField(L1[i]);
  993. F2:=TField(L2[I]);
  994. if F2.IsNull then
  995. F1.Clear
  996. else
  997. Case F1.DataType of
  998. ftFixedChar,
  999. ftString : F1.AsString:=F2.AsString;
  1000. ftBoolean : F1.AsBoolean:=F2.AsBoolean;
  1001. ftFloat : F1.AsFloat:=F2.AsFloat;
  1002. ftLargeInt : F1.AsLargeInt:=F2.AsLargeInt;
  1003. ftSmallInt,
  1004. ftInteger,
  1005. ftShortInt,
  1006. ftByte : F1.AsInteger:=F2.AsInteger;
  1007. ftDate : F1.AsDateTime:=F2.AsDateTime;
  1008. ftTime : F1.AsDateTime:=F2.AsDateTime;
  1009. ftDateTime : F1.AsDateTime:=F2.AsDateTime;
  1010. ftLongWord : F1.AsLongWord:=F2.AsLongWord;
  1011. ftExtended : F1.AsExtended:=F2.AsExtended;
  1012. ftSingle : F1.AsSingle:=F2.AsSingle;
  1013. else F1.AsString:=F2.AsString;
  1014. end;
  1015. end;
  1016. Try
  1017. Post;
  1018. except
  1019. Cancel;
  1020. Raise;
  1021. end;
  1022. Dataset.Next;
  1023. end;
  1024. Finally
  1025. DataSet.GotoBookmark(OriginalPosition); //Return to original record
  1026. Dataset.EnableControls;
  1027. EnableControls;
  1028. end;
  1029. finally
  1030. L2.Free;
  1031. end;
  1032. finally
  1033. l1.Free;
  1034. end;
  1035. end;
  1036. end;
  1037. function TMemDataset.GetRecordBufferPointer(p:TRecordBuffer; Pos:Integer):TRecordBuffer;
  1038. begin
  1039. Result:=p;
  1040. inc(Result, Pos);
  1041. end;
  1042. function TMemDataset.GetIntegerPointer(p:PInteger; Pos:Integer):PInteger;
  1043. begin
  1044. Result:=p;
  1045. inc(Result, Pos);
  1046. end;
  1047. function TMemDataset.MDSLocateRecord(const KeyFields: string; const KeyValues: Variant;
  1048. Options: TLocateOptions; out ARecNo: integer): Boolean;
  1049. var
  1050. SaveState: TDataSetState;
  1051. lstKeyFields: TList;
  1052. Matched: boolean;
  1053. AKeyValues: variant;
  1054. i: integer;
  1055. AField: TField;
  1056. s1,s2: UTF8String;
  1057. begin
  1058. Result := false;
  1059. SaveState := SetTempState(dsFilter);
  1060. FFilterBuffer := TempBuffer;
  1061. lstKeyFields := TList.Create;
  1062. try
  1063. GetFieldList(lstKeyFields, KeyFields);
  1064. if VarArrayDimCount(KeyValues) = 0 then
  1065. begin
  1066. Matched := lstKeyFields.Count = 1;
  1067. AKeyValues := VarArrayOf([KeyValues]);
  1068. end
  1069. else if VarArrayDimCount(KeyValues) = 1 then
  1070. begin
  1071. Matched := VarArrayHighBound(KeyValues,1) + 1 = lstKeyFields.Count;
  1072. AKeyValues := KeyValues;
  1073. end
  1074. else
  1075. Matched := false;
  1076. if Matched then
  1077. begin
  1078. ARecNo:=0;
  1079. while ARecNo<FRecCount do
  1080. begin
  1081. MDSReadRecord(FFilterBuffer, ARecNo);
  1082. if Filtered then
  1083. Result:=MDSFilterRecord(FFilterBuffer)
  1084. else
  1085. Result:=true;
  1086. // compare field by field
  1087. i:=0;
  1088. while Result and (i<lstKeyFields.Count) do
  1089. begin
  1090. AField := TField(lstKeyFields[i]);
  1091. // string fields
  1092. if AField.DataType in [ftString, ftFixedChar] then
  1093. begin
  1094. if TStringField(AField).CodePage=CP_UTF8 then
  1095. begin
  1096. s1 := AField.AsUTF8String;
  1097. s2 := UTF8Encode(VarToUnicodeStr(AKeyValues[i]));
  1098. end
  1099. else
  1100. begin
  1101. s1 := AField.AsString;
  1102. s2 := VarToStr(AKeyValues[i]);
  1103. end;
  1104. if loPartialKey in Options then
  1105. s1 := copy(s1, 1, length(s2));
  1106. if loCaseInsensitive in Options then
  1107. Result := AnsiCompareText(s1, s2)=0
  1108. else
  1109. Result := s1=s2;
  1110. end
  1111. // all other fields
  1112. else
  1113. Result := AField.Value=AKeyValues[i];
  1114. inc(i);
  1115. end;
  1116. if Result then
  1117. break;
  1118. inc(ARecNo);
  1119. end;
  1120. end;
  1121. finally
  1122. lstKeyFields.Free;
  1123. RestoreState(SaveState);
  1124. end;
  1125. end;
  1126. procedure TMemDataset.SetFilterText(AValue: string);
  1127. begin
  1128. // Just do nothing; filter is not implemented
  1129. end;
  1130. function TMemDataset.Locate(const KeyFields: string; const KeyValues: Variant;
  1131. Options: TLocateOptions): boolean;
  1132. var
  1133. ARecNo: integer;
  1134. begin
  1135. // Call inherited to make sure the dataset is bi-directional
  1136. Result := inherited;
  1137. CheckActive;
  1138. Result:=MDSLocateRecord(KeyFields, KeyValues, Options, ARecNo);
  1139. if Result then begin
  1140. // TODO: generate scroll events if matched record is found
  1141. FCurrRecNo:=ARecNo;
  1142. Resync([]);
  1143. end;
  1144. end;
  1145. function TMemDataset.Lookup(const KeyFields: string; const KeyValues: Variant;
  1146. const ResultFields: string): Variant;
  1147. var
  1148. ARecNo: integer;
  1149. SaveState: TDataSetState;
  1150. begin
  1151. if MDSLocateRecord(KeyFields, KeyValues, [], ARecNo) then
  1152. begin
  1153. SaveState := SetTempState(dsCalcFields);
  1154. try
  1155. // FFilterBuffer contains found record
  1156. CalculateFields(FFilterBuffer); // CalcBuffer is set to FFilterBuffer
  1157. Result:=FieldValues[ResultFields];
  1158. finally
  1159. RestoreState(SaveState);
  1160. end;
  1161. end
  1162. else
  1163. Result:=Null;
  1164. end;
  1165. end.