bufdataset.inc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1999-2000 by Michael Van Canneyt, member of the
  5. Free Pascal development team
  6. BufDataset implementation
  7. See the file COPYING.FPC, included in this distribution,
  8. for details about the copyright.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. **********************************************************************}
  13. { ---------------------------------------------------------------------
  14. TBufDataSet
  15. ---------------------------------------------------------------------}
  16. constructor TBufDataset.Create(AOwner : TComponent);
  17. begin
  18. Inherited Create(AOwner);
  19. SetLength(FUpdateBuffer,0);
  20. // temporary set it here
  21. FPacketRecords := 10;
  22. end;
  23. destructor TBufDataset.Destroy;
  24. begin
  25. inherited destroy;
  26. end;
  27. Function TBufDataset.GetCanModify: Boolean;
  28. begin
  29. Result:= False;
  30. end;
  31. function TBufDataset.AllocRecordBuffer: PChar;
  32. begin
  33. result := AllocMem(FRecordsize + sizeof(TBufBookmark));
  34. end;
  35. procedure TBufDataset.FreeRecordBuffer(var Buffer: PChar);
  36. begin
  37. ReAllocMem(Buffer,0);
  38. end;
  39. procedure TBufDataset.InternalOpen;
  40. begin
  41. CalcRecordSize;
  42. FBRecordcount := 0;
  43. FBBuffercount := 0;
  44. FBCurrentrecord := -1;
  45. FOpen:=True;
  46. FIsEOF := false;
  47. FIsbOF := true;
  48. end;
  49. procedure TBufDataset.InternalClose;
  50. var i : integer;
  51. begin
  52. FOpen:=False;
  53. CancelUpdates;
  54. for i := 0 to FBRecordCount-1 do FreeRecordBuffer(FBBuffers[i]);
  55. If FBRecordCount > 0 then ReAllocMem(FBBuffers,0);
  56. FBRecordcount := 0;
  57. FBBuffercount := 0;
  58. FBCurrentrecord := -1;
  59. FIsEOF := true;
  60. FIsbOF := true;
  61. end;
  62. procedure TBufDataset.InternalFirst;
  63. begin
  64. FBCurrentRecord := -1;
  65. FIsEOF := false;
  66. end;
  67. procedure TBufDataset.InternalLast;
  68. begin
  69. repeat
  70. until getnextpacket < FPacketRecords;
  71. FIsBOF := false;
  72. FBCurrentRecord := FBRecordcount;
  73. end;
  74. function TBufDataset.GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
  75. var x : longint;
  76. RecUpdBuf : PRecUpdateBuffer;
  77. FieldUpdBuf : PFieldUpdateBuffer;
  78. NullMask : pbyte;
  79. begin
  80. Result := grOK;
  81. case GetMode of
  82. gmPrior :
  83. if FIsBOF then
  84. result := grBOF
  85. else if FBCurrentRecord <= 0 then
  86. begin
  87. Result := grBOF;
  88. FBCurrentRecord := -1;
  89. end
  90. else
  91. begin
  92. Dec(FBCurrentRecord);
  93. FIsEof := false;
  94. end;
  95. gmCurrent :
  96. if (FBCurrentRecord < 0) or (FBCurrentRecord >= RecordCount) then
  97. Result := grError;
  98. gmNext :
  99. if FIsEOF then
  100. result := grEOF
  101. else if FBCurrentRecord >= (FBRecordCount - 1) then
  102. begin
  103. if getnextpacket > 0 then
  104. begin
  105. Inc(FBCurrentRecord);
  106. FIsBof := false;
  107. end
  108. else
  109. begin
  110. FIsEOF := true;
  111. result:=grEOF;
  112. end
  113. end
  114. else
  115. begin
  116. Inc(FBCurrentRecord);
  117. FIsBof := false;
  118. end;
  119. end;
  120. if Result = grOK then
  121. begin
  122. with PBufBookmark(Buffer + RecordSize)^ do
  123. begin
  124. BookmarkData := FBCurrentRecord;
  125. BookmarkFlag := bfCurrent;
  126. end;
  127. move(FBBuffers[FBCurrentRecord]^,buffer^,RecordSize);
  128. // Cached Updates:
  129. If GetRecordUpdateBuffer(FBCurrentRecord,RecUpdBuf) then
  130. begin
  131. NullMask := pbyte(buffer);
  132. inc(buffer,FNullmaskSize);
  133. for x := 0 to FieldDefs.count-1 do
  134. begin
  135. if GetFieldUpdateBuffer(x,RecUpdBuf,FieldUpdBuf) then
  136. If not FieldUpdBuf^.IsNull then
  137. begin
  138. NullMask[x div 8] := (NullMask[x div 8]) and not (1 shl (x mod 8));
  139. move(FieldUpdBuf^.NewValue^,buffer^,GetFieldSize(FieldDefs[x]));
  140. end
  141. else
  142. NullMask[x div 8] := (NullMask[x div 8]) or (1 shl (x mod 8));
  143. Inc(Buffer, GetFieldSize(FieldDefs[x]));
  144. end;
  145. end;
  146. end
  147. else if (Result = grError) and doCheck then
  148. DatabaseError('No record');
  149. end;
  150. function TBufDataset.GetRecordUpdateBuffer(rno : integer;var RecUpdBuf : PRecUpdateBuffer) : boolean;
  151. var r : integer;
  152. begin
  153. Result := False;
  154. for r := 0 to high(FUpdateBuffer) do
  155. if (FUpdateBuffer[r].RecordNo = rno) and (@FUpdateBuffer[r] <> FEditBuf) then // Neglect the edit-buffer
  156. begin
  157. RecUpdBuf := @FUpdateBuffer[r];
  158. Result := True;
  159. Break;
  160. end;
  161. end;
  162. function TBufDataset.GetFieldUpdateBuffer(fieldno : integer;RecUpdBuf : PRecUpdateBuffer;var FieldUpdBuf : pFieldUpdateBuffer) : boolean;
  163. var f : integer;
  164. begin
  165. Result := False;
  166. for f := 0 to High(RecUpdBuf^.FieldsUpdateBuffer) do
  167. if RecUpdBuf^.FieldsUpdateBuffer[f].FieldNo = fieldno then
  168. begin
  169. FieldUpdBuf := @RecUpdBuf^.FieldsUpdateBuffer[f];
  170. Result := True;
  171. Break;
  172. end;
  173. end;
  174. procedure TBufDataset.InternalSetToRecord(Buffer: PChar);
  175. begin
  176. FBCurrentRecord := PBufBookmark(Buffer + RecordSize)^.BookmarkData;
  177. FIsEOF := False;
  178. FIsBOF := False;
  179. end;
  180. procedure TBufDataset.SetBookmarkData(Buffer: PChar; Data: Pointer);
  181. begin
  182. PBufBookmark(Buffer + RecordSize)^.BookmarkData := PInteger(Data)^;
  183. end;
  184. procedure TBufDataset.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
  185. begin
  186. PBufBookmark(Buffer + RecordSize)^.BookmarkFlag := Value;
  187. end;
  188. procedure TBufDataset.GetBookmarkData(Buffer: PChar; Data: Pointer);
  189. begin
  190. PInteger(Data)^ := PBufBookmark(Buffer + RecordSize)^.BookmarkData;
  191. end;
  192. function TBufDataset.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
  193. begin
  194. Result := PBufBookmark(Buffer + RecordSize)^.BookmarkFlag;
  195. end;
  196. procedure TBufDataset.InternalGotoBookmark(ABookmark: Pointer);
  197. begin
  198. FBCurrentRecord := Plongint(ABookmark)^;
  199. FIsEOF := False;
  200. FIsBOF := False;
  201. end;
  202. function TBufDataset.getnextpacket : integer;
  203. var i : integer;
  204. b : boolean;
  205. begin
  206. i := 0;
  207. if FPacketRecords > 0 then
  208. begin
  209. FBBufferCount := FBBuffercount + FPacketRecords;
  210. ReAllocMem(FBBuffers,FBBuffercount*SizeOf(PChar));
  211. repeat
  212. FBBuffers[FBRecordCount+i] := AllocRecordBuffer;
  213. b := (loadbuffer(FBBuffers[FBRecordCount+i])<>grOk);
  214. inc(i);
  215. until (i = FPacketRecords) or b;
  216. if b then
  217. begin
  218. dec(i);
  219. FreeRecordBuffer(FBBuffers[FBRecordCount+i]);
  220. end;
  221. FBRecordCount := FBRecordCount + i;
  222. end;
  223. result := i;
  224. end;
  225. function TBufDataset.GetFieldSize(FieldDef : TFieldDef) : longint;
  226. begin
  227. case FieldDef.DataType of
  228. ftString : result := FieldDef.Size + 1;
  229. ftSmallint,
  230. ftInteger,
  231. ftword : result := sizeof(longint);
  232. ftBoolean : result := sizeof(boolean);
  233. ftBCD : result := sizeof(currency);
  234. ftFloat : result := sizeof(double);
  235. ftTime,
  236. ftDate,
  237. ftDateTime : result := sizeof(TDateTime)
  238. else Result := 10
  239. end;
  240. end;
  241. function TBufDataset.LoadBuffer(Buffer : PChar): TGetResult;
  242. var NullMask : pbyte;
  243. x : longint;
  244. begin
  245. if not Fetch then
  246. begin
  247. Result := grEOF;
  248. Exit;
  249. end;
  250. NullMask := pointer(buffer);
  251. fillchar(Nullmask^,FNullmaskSize,0);
  252. inc(buffer,FNullmaskSize);
  253. for x := 0 to FieldDefs.count-1 do
  254. begin
  255. if not LoadField(FieldDefs[x],buffer) then
  256. NullMask[x div 8] := (NullMask[x div 8]) or (1 shl (x mod 8));
  257. inc(buffer,GetFieldSize(FieldDefs[x]));
  258. end;
  259. Result := grOK;
  260. end;
  261. function TBufDataset.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
  262. var
  263. x : longint;
  264. CurrBuff : pchar;
  265. begin
  266. Result := False;
  267. If Field.Fieldno > 0 then // If = 0, then calculated field or something similar
  268. begin
  269. if state = dsOldValue then
  270. CurrBuff := FBBuffers[GetRecNo]
  271. else
  272. CurrBuff := ActiveBuffer;
  273. if ord(currbuff[(Field.Fieldno-1) div 8]) and (1 shl ((Field.Fieldno-1) mod 8)) > 0 then
  274. begin
  275. result := false;
  276. exit;
  277. end;
  278. inc(Currbuff,FNullmaskSize);
  279. for x := 0 to FieldDefs.count-1 do
  280. begin
  281. if (Field.FieldName = FieldDefs[x].Name) then
  282. begin
  283. Move(CurrBuff^, Buffer^, GetFieldSize(FieldDefs[x]));
  284. Result := True;
  285. Break;
  286. end
  287. else Inc(CurrBuff, GetFieldSize(FieldDefs[x]));
  288. end;
  289. end;
  290. end;
  291. procedure TBufDataset.SetFieldData(Field: TField; Buffer: Pointer);
  292. var
  293. x : longint;
  294. CurrBuff : pointer;
  295. NullMask : pbyte;
  296. FieldUpdBuf : PFieldUpdateBuffer;
  297. begin
  298. If Field.Fieldno > 0 then // If = 0, then calculated field or something
  299. begin
  300. CurrBuff := ActiveBuffer;
  301. NullMask := CurrBuff;
  302. inc(Currbuff,FNullmaskSize);
  303. for x := 0 to FieldDefs.count-1 do
  304. begin
  305. if (Field.FieldName = FieldDefs[x].Name) then
  306. begin
  307. if assigned(buffer) then
  308. begin
  309. Move(Buffer^, CurrBuff^, GetFieldSize(FieldDefs[x]));
  310. NullMask[x div 8] := (NullMask[x div 8]) and not (1 shl (x mod 8));
  311. end
  312. else
  313. NullMask[x div 8] := (NullMask[x div 8]) or (1 shl (x mod 8));
  314. // cached updates
  315. with FEditBuf^ do
  316. begin
  317. if not GetFieldUpdateBuffer(x,FEditBuf,FieldUpdBuf) then
  318. begin
  319. SetLength(FieldsUpdateBuffer,length(FieldsUpdateBuffer)+1);
  320. FieldUpdBuf := @FieldsUpdateBuffer[high(FieldsUpdateBuffer)];
  321. GetMem(FieldUpdBuf^.NewValue,GetFieldSize(FieldDefs[x]));
  322. FieldUpdBuf^.FieldNo := x;
  323. end;
  324. if assigned(buffer) then
  325. begin
  326. Move(Buffer^, FieldUpdBuf^.NewValue^, GetFieldSize(FieldDefs[x]));
  327. FieldUpdBuf^.IsNull := False;
  328. end
  329. else FieldUpdBuf^.IsNull := True;
  330. end;
  331. Break;
  332. end
  333. else Inc(CurrBuff, GetFieldSize(FieldDefs[x]));
  334. end;
  335. if not (State in [dsCalcFields, dsFilter, dsNewValue]) then
  336. DataEvent(deFieldChange, Ptrint(Field));
  337. end;
  338. end;
  339. procedure TBufDataset.InternalEdit;
  340. begin
  341. if not GetRecordUpdateBuffer(recno,FEditBuf) then
  342. begin
  343. If not assigned(FEditBuf) then
  344. begin
  345. SetLength(FUpdateBuffer,length(FUpdateBuffer)+1);
  346. FEditBuf := @FUpdateBuffer[high(FUpdateBuffer)];
  347. end;
  348. FEditBuf^.RecordNo := getrecno;
  349. end;
  350. end;
  351. function TBufDataset.ApplyRecUpdate : boolean;
  352. begin
  353. Result := False;
  354. end;
  355. procedure TBufDataset.CancelUpdates;
  356. var r,f : integer;
  357. begin
  358. for r := 0 to high(FUpdateBuffer) do
  359. for f := 0 to high(FUpdateBuffer[r].FieldsUpdateBuffer) do
  360. FreeMem(FUpdateBuffer[r].FieldsUpdateBuffer[f].newvalue);
  361. SetLength(FUpdateBuffer,0);
  362. end;
  363. procedure TBufDataset.ApplyUpdates;
  364. var SaveBookmark : Integer;
  365. r,i : Integer;
  366. buffer : PChar;
  367. x : integer;
  368. FieldUpdBuf : PFieldUpdateBuffer;
  369. NullMask : pbyte;
  370. begin
  371. SaveBookMark := GetRecNo;
  372. r := 0;
  373. while r < Length(FUpdateBuffer) do
  374. begin
  375. if @FUpdateBuffer[r] <> FEditBuf then // Neglect edit-buffer
  376. begin
  377. SetRecNo(FUpdateBuffer[r].RecordNo);
  378. if ApplyRecUpdate then
  379. begin
  380. buffer := FBBuffers[FUpdateBuffer[r].RecordNo];
  381. NullMask := pbyte(buffer);
  382. inc(buffer,FNullmaskSize);
  383. for x := 0 to FieldDefs.count-1 do
  384. begin
  385. if GetFieldUpdateBuffer(x,@FUpdateBuffer[r],FieldUpdBuf) then
  386. If not FieldUpdBuf^.IsNull then
  387. begin
  388. NullMask[x div 8] := (NullMask[x div 8]) and not (1 shl (x mod 8));
  389. move(FieldUpdBuf^.NewValue^,buffer^,GetFieldSize(FieldDefs[x]));
  390. FreeMem(FieldUpdBuf^.NewValue);
  391. end
  392. else
  393. NullMask[x div 8] := (NullMask[x div 8]) or (1 shl (x mod 8));
  394. Inc(Buffer, GetFieldSize(FieldDefs[x]));
  395. end;
  396. for i := r to high(FUpdateBuffer)-1 do
  397. FUpdateBuffer[i] := FupdateBuffer[i+1];
  398. dec(r);
  399. SetLength(FUpdateBuffer,high(FUpdateBuffer));
  400. end;
  401. end;
  402. inc(r);
  403. end;
  404. Refresh;
  405. SetRecNo(SaveBookMark);
  406. end;
  407. procedure TBufDataset.InternalPost;
  408. begin
  409. if state=dsEdit then
  410. begin
  411. if Length(FUpdateBuffer[High(FUpdateBuffer)].FieldsUpdateBuffer) > 0 then
  412. FEditBuf := nil;
  413. end;
  414. end;
  415. procedure TBufDataset.CalcRecordSize;
  416. var x : longint;
  417. begin
  418. FNullmaskSize := 1+((FieldDefs.count-1) div 8);
  419. FRecordSize := FNullmaskSize;
  420. for x := 0 to FieldDefs.count-1 do
  421. inc(FRecordSize, GetFieldSize(FieldDefs[x]));
  422. end;
  423. function TBufDataset.GetRecordSize : Word;
  424. begin
  425. result := FRecordSize;
  426. end;
  427. procedure TBufDataset.InternalInitRecord(Buffer: PChar);
  428. begin
  429. FillChar(Buffer^, FRecordSize, #0);
  430. end;
  431. procedure TBufDataset.SetRecNo(Value: Longint);
  432. begin
  433. GotoBookmark(@value);
  434. end;
  435. function TBufDataset.GetRecNo: Longint;
  436. begin
  437. GetBookmarkData(ActiveBuffer,@Result);
  438. end;
  439. function TBufDataset.IsCursorOpen: Boolean;
  440. begin
  441. Result := FOpen;
  442. end;
  443. Function TBufDataset.GetRecordCount: Longint;
  444. begin
  445. Result := FBRecordCount;
  446. end;