bufdataset.inc 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 1999-2000 by Michael Van Canneyt, member of the
  4. Free Pascal development team
  5. BufDataset implementation
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. { ---------------------------------------------------------------------
  13. TBufDataSet
  14. ---------------------------------------------------------------------}
  15. constructor TBufDataset.Create(AOwner : TComponent);
  16. begin
  17. Inherited Create(AOwner);
  18. SetLength(FUpdateBuffer,0);
  19. BookmarkSize := sizeof(TBufBookmark);
  20. FPacketRecords := 10;
  21. end;
  22. procedure TBufDataset.SetPacketRecords(aValue : integer);
  23. begin
  24. if (aValue = -1) or (aValue > 0) then FPacketRecords := aValue
  25. else DatabaseError(SInvPacketRecordsValue);
  26. end;
  27. destructor TBufDataset.Destroy;
  28. begin
  29. inherited destroy;
  30. end;
  31. Function TBufDataset.GetCanModify: Boolean;
  32. begin
  33. Result:= False;
  34. end;
  35. function TBufDataset.intAllocRecordBuffer: PChar;
  36. begin
  37. // Note: Only the internal buffers of TDataset provide bookmark information
  38. result := AllocMem(FRecordsize+sizeof(TBufRecLinkItem));
  39. end;
  40. function TBufDataset.AllocRecordBuffer: PChar;
  41. begin
  42. result := AllocMem(FRecordsize + sizeof(TBufBookmark));
  43. end;
  44. procedure TBufDataset.FreeRecordBuffer(var Buffer: PChar);
  45. begin
  46. ReAllocMem(Buffer,0);
  47. end;
  48. procedure TBufDataset.InternalOpen;
  49. begin
  50. CalcRecordSize;
  51. FBRecordcount := 0;
  52. FFirstRecBuf := pointer(IntAllocRecordBuffer);
  53. FLastRecBuf := FFirstRecBuf;
  54. FCurrentRecBuf := FLastRecBuf;
  55. FAllPacketsFetched := False;
  56. FOpen:=True;
  57. end;
  58. procedure TBufDataset.InternalClose;
  59. var pc : pchar;
  60. begin
  61. FOpen:=False;
  62. FCurrentRecBuf := FFirstRecBuf;
  63. SetLength(FUpdateBuffer,0);
  64. while assigned(FCurrentRecBuf) do
  65. begin
  66. pc := pointer(FCurrentRecBuf);
  67. FCurrentRecBuf := FCurrentRecBuf^.next;
  68. FreeRecordBuffer(pc);
  69. end;
  70. FFirstRecBuf:= nil;
  71. SetLength(FFieldBufPositions,0);
  72. end;
  73. procedure TBufDataset.InternalFirst;
  74. begin
  75. // if FCurrentRecBuf = FLastRecBuf then the dataset is just opened and empty
  76. // in which case InternalFirst should do nothing (bug 7211)
  77. if FCurrentRecBuf <> FLastRecBuf then
  78. FCurrentRecBuf := nil;
  79. end;
  80. procedure TBufDataset.InternalLast;
  81. begin
  82. repeat
  83. until (getnextpacket < FPacketRecords) or (FPacketRecords = -1);
  84. if FLastRecBuf <> FFirstRecBuf then
  85. FCurrentRecBuf := FLastRecBuf;
  86. end;
  87. procedure unSetFieldIsNull(NullMask : pbyte;x : longint); //inline;
  88. begin
  89. NullMask[x div 8] := (NullMask[x div 8]) and not (1 shl (x mod 8));
  90. end;
  91. procedure SetFieldIsNull(NullMask : pbyte;x : longint); //inline;
  92. begin
  93. NullMask[x div 8] := (NullMask[x div 8]) or (1 shl (x mod 8));
  94. end;
  95. function GetFieldIsNull(NullMask : pbyte;x : longint) : boolean; //inline;
  96. begin
  97. result := ord(NullMask[x div 8]) and (1 shl (x mod 8)) > 0
  98. end;
  99. function TBufDataset.GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
  100. begin
  101. Result := grOK;
  102. case GetMode of
  103. gmPrior :
  104. if not assigned(PBufRecLinkItem(FCurrentRecBuf)^.prior) then
  105. begin
  106. Result := grBOF;
  107. end
  108. else
  109. begin
  110. FCurrentRecBuf := PBufRecLinkItem(FCurrentRecBuf)^.prior;
  111. end;
  112. gmCurrent :
  113. if FCurrentRecBuf = FLastRecBuf then
  114. Result := grError;
  115. gmNext :
  116. if FCurrentRecBuf = FLastRecBuf then // Dataset is empty (just opened)
  117. begin
  118. if getnextpacket = 0 then result := grEOF;
  119. end
  120. else if FCurrentRecBuf = nil then FCurrentRecBuf := FFirstRecBuf
  121. else if (PBufRecLinkItem(FCurrentRecBuf)^.next = FLastRecBuf) then
  122. begin
  123. if getnextpacket > 0 then
  124. begin
  125. FCurrentRecBuf := PBufRecLinkItem(FCurrentRecBuf)^.next;
  126. end
  127. else
  128. begin
  129. result:=grEOF;
  130. end
  131. end
  132. else
  133. begin
  134. FCurrentRecBuf := PBufRecLinkItem(FCurrentRecBuf)^.next;
  135. end;
  136. end;
  137. if Result = grOK then
  138. begin
  139. with PBufBookmark(Buffer + FRecordSize)^ do
  140. begin
  141. BookmarkData := FCurrentRecBuf;
  142. BookmarkFlag := bfCurrent;
  143. end;
  144. move((pointer(FCurrentRecBuf)+sizeof(TBufRecLinkItem))^,buffer^,FRecordSize);
  145. end
  146. else if (Result = grError) and doCheck then
  147. DatabaseError('No record');
  148. end;
  149. function TBufDataset.GetRecordUpdateBuffer : boolean;
  150. var x : integer;
  151. CurrBuff : PChar;
  152. begin
  153. GetBookmarkData(ActiveBuffer,@CurrBuff);
  154. if (FCurrentUpdateBuffer >= length(FUpdateBuffer)) or (FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData <> CurrBuff) then
  155. for x := 0 to high(FUpdateBuffer) do
  156. if FUpdateBuffer[x].BookmarkData = CurrBuff then
  157. begin
  158. FCurrentUpdateBuffer := x;
  159. break;
  160. end;
  161. Result := (FCurrentUpdateBuffer < length(FUpdateBuffer)) and (FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData = CurrBuff);
  162. end;
  163. procedure TBufDataset.InternalSetToRecord(Buffer: PChar);
  164. begin
  165. FCurrentRecBuf := PBufBookmark(Buffer + FRecordSize)^.BookmarkData;
  166. end;
  167. procedure TBufDataset.SetBookmarkData(Buffer: PChar; Data: Pointer);
  168. begin
  169. PBufBookmark(Buffer + FRecordSize)^.BookmarkData := pointer(Data^);
  170. end;
  171. procedure TBufDataset.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
  172. begin
  173. PBufBookmark(Buffer + FRecordSize)^.BookmarkFlag := Value;
  174. end;
  175. procedure TBufDataset.GetBookmarkData(Buffer: PChar; Data: Pointer);
  176. begin
  177. pointer(Data^) := PBufBookmark(Buffer + FRecordSize)^.BookmarkData;
  178. end;
  179. function TBufDataset.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
  180. begin
  181. Result := PBufBookmark(Buffer + FRecordSize)^.BookmarkFlag;
  182. end;
  183. procedure TBufDataset.InternalGotoBookmark(ABookmark: Pointer);
  184. begin
  185. // note that ABookMark should be a PBufBookmark. But this way it can also be
  186. // a pointer to a TBufRecLinkItem
  187. FCurrentRecBuf := pointer(ABookmark^);
  188. end;
  189. function TBufDataset.getnextpacket : integer;
  190. var i : integer;
  191. pb : pchar;
  192. begin
  193. if FAllPacketsFetched then
  194. begin
  195. result := 0;
  196. exit;
  197. end;
  198. i := 0;
  199. pb := pchar(pointer(FLastRecBuf)+sizeof(TBufRecLinkItem));
  200. while ((i < FPacketRecords) or (FPacketRecords = -1)) and (loadbuffer(pb) = grOk) do
  201. begin
  202. FLastRecBuf^.next := pointer(IntAllocRecordBuffer);
  203. FLastRecBuf^.next^.prior := FLastRecBuf;
  204. FLastRecBuf := FLastRecBuf^.next;
  205. pb := pchar(pointer(FLastRecBuf)+sizeof(TBufRecLinkItem));
  206. inc(i);
  207. end;
  208. FBRecordCount := FBRecordCount + i;
  209. result := i;
  210. end;
  211. function TBufDataset.GetFieldSize(FieldDef : TFieldDef) : longint;
  212. begin
  213. case FieldDef.DataType of
  214. ftString,
  215. ftFixedChar: result := FieldDef.Size + 1;
  216. ftSmallint,
  217. ftInteger,
  218. ftword : result := sizeof(longint);
  219. ftBoolean : result := sizeof(wordbool);
  220. ftBCD : result := sizeof(currency);
  221. ftFloat : result := sizeof(double);
  222. ftLargeInt : result := sizeof(largeint);
  223. ftTime,
  224. ftDate,
  225. ftDateTime : result := sizeof(TDateTime)
  226. else Result := 10
  227. end;
  228. end;
  229. function TBufDataset.LoadBuffer(Buffer : PChar): TGetResult;
  230. var NullMask : pbyte;
  231. x : longint;
  232. begin
  233. if not Fetch then
  234. begin
  235. Result := grEOF;
  236. FAllPacketsFetched := True;
  237. Exit;
  238. end;
  239. NullMask := pointer(buffer);
  240. fillchar(Nullmask^,FNullmaskSize,0);
  241. inc(buffer,FNullmaskSize);
  242. for x := 0 to FieldDefs.count-1 do
  243. begin
  244. if not LoadField(FieldDefs[x],buffer) then
  245. SetFieldIsNull(NullMask,x);
  246. inc(buffer,GetFieldSize(FieldDefs[x]));
  247. end;
  248. Result := grOK;
  249. end;
  250. function TBufDataset.GetFieldData(Field: TField; Buffer: Pointer;
  251. NativeFormat: Boolean): Boolean;
  252. begin
  253. Result := GetFieldData(Field, Buffer);
  254. end;
  255. function TBufDataset.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
  256. var CurrBuff : pchar;
  257. begin
  258. Result := False;
  259. If Field.Fieldno > 0 then // If = 0, then calculated field or something similar
  260. begin
  261. if state = dsOldValue then
  262. begin
  263. if not GetRecordUpdateBuffer then
  264. begin
  265. // There is no old value available
  266. result := false;
  267. exit;
  268. end;
  269. currbuff := FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer+sizeof(TBufRecLinkItem);
  270. end
  271. else
  272. begin
  273. CurrBuff := ActiveBuffer;
  274. if not assigned(CurrBuff) then
  275. begin
  276. result := false;
  277. exit;
  278. end;
  279. end;
  280. if GetFieldIsnull(pbyte(CurrBuff),Field.Fieldno-1) then
  281. begin
  282. result := false;
  283. exit;
  284. end;
  285. inc(CurrBuff,FFieldBufPositions[Field.FieldNo-1]);
  286. if assigned(buffer) then Move(CurrBuff^, Buffer^, GetFieldSize(FieldDefs[Field.FieldNo-1]));
  287. Result := True;
  288. end;
  289. end;
  290. procedure TBufDataset.SetFieldData(Field: TField; Buffer: Pointer;
  291. NativeFormat: Boolean);
  292. begin
  293. SetFieldData(Field,Buffer);
  294. end;
  295. procedure TBufDataset.SetFieldData(Field: TField; Buffer: Pointer);
  296. var CurrBuff : pointer;
  297. NullMask : pbyte;
  298. begin
  299. if not (state in [dsEdit, dsInsert, dsFilter]) then
  300. begin
  301. DatabaseErrorFmt(SNotInEditState,[NAme],self);
  302. exit;
  303. end;
  304. If Field.Fieldno > 0 then // If = 0, then calculated field or something
  305. begin
  306. if state = dsFilter then // Set the value into the 'temporary' FLastRecBuf buffer for Locate and Lookup
  307. CurrBuff := pointer(FLastRecBuf) + sizeof(TBufRecLinkItem)
  308. else
  309. CurrBuff := ActiveBuffer;
  310. NullMask := CurrBuff;
  311. inc(CurrBuff,FFieldBufPositions[Field.FieldNo-1]);
  312. if assigned(buffer) then
  313. begin
  314. Move(Buffer^, CurrBuff^, GetFieldSize(FieldDefs[Field.FieldNo-1]));
  315. unSetFieldIsNull(NullMask,Field.FieldNo-1);
  316. end
  317. else
  318. SetFieldIsNull(NullMask,Field.FieldNo-1);
  319. if not (State in [dsCalcFields, dsFilter, dsNewValue]) then
  320. DataEvent(deFieldChange, Ptrint(Field));
  321. end;
  322. end;
  323. procedure TBufDataset.InternalDelete;
  324. begin
  325. GetBookmarkData(ActiveBuffer,@FCurrentRecBuf);
  326. if FCurrentRecBuf <> FFirstRecBuf then FCurrentRecBuf^.prior^.next := FCurrentRecBuf^.next
  327. else FFirstRecBuf := FCurrentRecBuf^.next;
  328. FCurrentRecBuf^.next^.prior := FCurrentRecBuf^.prior;
  329. if not GetRecordUpdateBuffer then
  330. begin
  331. FCurrentUpdateBuffer := length(FUpdateBuffer);
  332. SetLength(FUpdateBuffer,FCurrentUpdateBuffer+1);
  333. FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer := pchar(FCurrentRecBuf);
  334. FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData := FCurrentRecBuf;
  335. FCurrentRecBuf := FCurrentRecBuf^.next;
  336. end
  337. else
  338. begin
  339. if FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind = ukModify then
  340. begin
  341. FCurrentRecBuf := FCurrentRecBuf^.next;
  342. FreeRecordBuffer(pchar(FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData));
  343. FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData := FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer;
  344. end
  345. else
  346. begin
  347. FCurrentRecBuf := FCurrentRecBuf^.next;
  348. FreeRecordBuffer(pchar(FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData));
  349. FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData := nil; //this 'disables' the updatebuffer
  350. end;
  351. end;
  352. dec(FBRecordCount);
  353. FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind := ukDelete;
  354. end;
  355. procedure TBufDataset.ApplyRecUpdate(UpdateKind : TUpdateKind);
  356. begin
  357. raise EDatabaseError.Create(SApplyRecNotSupported);
  358. end;
  359. procedure TBufDataset.CancelUpdates;
  360. var r : Integer;
  361. begin
  362. CheckBrowseMode;
  363. if Length(FUpdateBuffer) > 0 then
  364. begin
  365. r := Length(FUpdateBuffer) -1;
  366. while r > -1 do with FUpdateBuffer[r] do
  367. begin
  368. if assigned(FUpdateBuffer[r].BookmarkData) then
  369. begin
  370. if UpdateKind = ukModify then
  371. begin
  372. move(pchar(OldValuesBuffer+sizeof(TBufRecLinkItem))^,pchar(BookmarkData+sizeof(TBufRecLinkItem))^,FRecordSize);
  373. FreeRecordBuffer(OldValuesBuffer);
  374. end
  375. else if UpdateKind = ukDelete then
  376. begin
  377. if assigned(PBufRecLinkItem(BookmarkData)^.prior) then // or else it was the first record
  378. PBufRecLinkItem(BookmarkData)^.prior^.next := BookmarkData
  379. else
  380. FFirstRecBuf := BookmarkData;
  381. PBufRecLinkItem(BookmarkData)^.next^.prior := BookmarkData;
  382. inc(FBRecordCount);
  383. end
  384. else if UpdateKind = ukInsert then
  385. begin
  386. if assigned(PBufRecLinkItem(BookmarkData)^.prior) then // or else it was the first record
  387. PBufRecLinkItem(BookmarkData)^.prior^.next := PBufRecLinkItem(BookmarkData)^.next
  388. else
  389. FFirstRecBuf := PBufRecLinkItem(BookmarkData)^.next;
  390. PBufRecLinkItem(BookmarkData)^.next^.prior := PBufRecLinkItem(BookmarkData)^.prior;
  391. // resync won't work if the currentbuffer is freed...
  392. if FCurrentRecBuf = BookmarkData then FCurrentRecBuf := FCurrentRecBuf^.next;
  393. FreeRecordBuffer(BookmarkData);
  394. dec(FBRecordCount);
  395. end;
  396. end;
  397. dec(r)
  398. end;
  399. SetLength(FUpdateBuffer,0);
  400. Resync([]);
  401. end;
  402. end;
  403. procedure TBufDataset.SetOnUpdateError(const AValue: TResolverErrorEvent);
  404. begin
  405. FOnUpdateError := AValue;
  406. end;
  407. procedure TBufDataset.ApplyUpdates; // For backwards-compatibility
  408. begin
  409. ApplyUpdates(0);
  410. end;
  411. procedure TBufDataset.ApplyUpdates(MaxErrors: Integer);
  412. var SaveBookmark : pchar;
  413. r : Integer;
  414. FailedCount : integer;
  415. EUpdErr : EUpdateError;
  416. Response : TResolverResponse;
  417. begin
  418. CheckBrowseMode;
  419. // There is no bookmark available if the dataset is empty
  420. if not IsEmpty then
  421. GetBookmarkData(ActiveBuffer,@SaveBookmark);
  422. r := 0;
  423. FailedCount := 0;
  424. Response := rrApply;
  425. while (r < Length(FUpdateBuffer)) and (Response <> rrAbort) do
  426. begin
  427. if assigned(FUpdateBuffer[r].BookmarkData) then
  428. begin
  429. InternalGotoBookmark(@FUpdateBuffer[r].BookmarkData);
  430. Resync([rmExact,rmCenter]);
  431. Response := rrApply;
  432. try
  433. ApplyRecUpdate(FUpdateBuffer[r].UpdateKind);
  434. except
  435. on E: EDatabaseError do
  436. begin
  437. Inc(FailedCount);
  438. if failedcount > word(MaxErrors) then Response := rrAbort
  439. else Response := rrSkip;
  440. EUpdErr := EUpdateError.Create(SOnUpdateError,E.Message,0,0,E);
  441. if assigned(FOnUpdateError) then FOnUpdateError(Self,Self,EUpdErr,FUpdateBuffer[r].UpdateKind,Response)
  442. else if Response = rrAbort then Raise EUpdErr
  443. end
  444. else
  445. raise;
  446. end;
  447. if response = rrApply then
  448. begin
  449. FreeRecordBuffer(FUpdateBuffer[r].OldValuesBuffer);
  450. FUpdateBuffer[r].BookmarkData := nil;
  451. end
  452. end;
  453. inc(r);
  454. end;
  455. if failedcount = 0 then
  456. SetLength(FUpdateBuffer,0);
  457. if not IsEmpty then
  458. begin
  459. InternalGotoBookMark(@SaveBookMark);
  460. Resync([rmExact,rmCenter]);
  461. end
  462. else
  463. InternalFirst;
  464. end;
  465. procedure TBufDataset.InternalPost;
  466. Var tmpRecBuffer : PBufRecLinkItem;
  467. CurrBuff : PChar;
  468. begin
  469. if state = dsInsert then
  470. begin
  471. if GetBookmarkFlag(ActiveBuffer) = bfEOF then
  472. // Append
  473. FCurrentRecBuf := FLastRecBuf
  474. else
  475. // The active buffer is the newly created TDataset record,
  476. // from which the bookmark is set to the record where the new record should be
  477. // inserted
  478. GetBookmarkData(ActiveBuffer,@FCurrentRecBuf);
  479. // Create the new record buffer
  480. tmpRecBuffer := FCurrentRecBuf^.prior;
  481. FCurrentRecBuf^.prior := pointer(IntAllocRecordBuffer);
  482. FCurrentRecBuf^.prior^.next := FCurrentRecBuf;
  483. FCurrentRecBuf := FCurrentRecBuf^.prior;
  484. If assigned(tmpRecBuffer) then // if not, it's the first record
  485. begin
  486. FCurrentRecBuf^.prior := tmpRecBuffer;
  487. tmpRecBuffer^.next := FCurrentRecBuf
  488. end
  489. else
  490. FFirstRecBuf := FCurrentRecBuf;
  491. // Link the newly created record buffer to the newly created TDataset record
  492. with PBufBookmark(ActiveBuffer + FRecordSize)^ do
  493. begin
  494. BookmarkData := FCurrentRecBuf;
  495. BookmarkFlag := bfInserted;
  496. end;
  497. inc(FBRecordCount);
  498. end
  499. else
  500. GetBookmarkData(ActiveBuffer,@FCurrentRecBuf);
  501. if not GetRecordUpdateBuffer then
  502. begin
  503. FCurrentUpdateBuffer := length(FUpdateBuffer);
  504. SetLength(FUpdateBuffer,FCurrentUpdateBuffer+1);
  505. FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData := FCurrentRecBuf;
  506. if state = dsEdit then
  507. begin
  508. // Update the oldvalues-buffer
  509. FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer := intAllocRecordBuffer;
  510. move(FCurrentRecBuf^,FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer^,FRecordSize+sizeof(TBufRecLinkItem));
  511. FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind := ukModify;
  512. end
  513. else
  514. FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind := ukInsert;
  515. end;
  516. CurrBuff := pchar(FCurrentRecBuf);
  517. inc(Currbuff,sizeof(TBufRecLinkItem));
  518. move(ActiveBuffer^,CurrBuff^,FRecordSize);
  519. end;
  520. procedure TBufDataset.CalcRecordSize;
  521. var x : longint;
  522. begin
  523. FNullmaskSize := 1+((FieldDefs.count-1) div 8);
  524. FRecordSize := FNullmaskSize;
  525. SetLength(FFieldBufPositions,FieldDefs.count);
  526. for x := 0 to FieldDefs.count-1 do
  527. begin
  528. FFieldBufPositions[x] := FRecordSize;
  529. inc(FRecordSize, GetFieldSize(FieldDefs[x]));
  530. end;
  531. end;
  532. function TBufDataset.GetRecordSize : Word;
  533. begin
  534. result := FRecordSize;
  535. end;
  536. function TBufDataset.GetChangeCount: integer;
  537. begin
  538. result := length(FUpdateBuffer);
  539. end;
  540. procedure TBufDataset.InternalInitRecord(Buffer: PChar);
  541. begin
  542. FillChar(Buffer^, FRecordSize, #0);
  543. fillchar(Buffer^,FNullmaskSize,255);
  544. end;
  545. procedure TBufDataset.SetRecNo(Value: Longint);
  546. var recnr : integer;
  547. TmpRecBuffer : PBufRecLinkItem;
  548. begin
  549. checkbrowsemode;
  550. if value > RecordCount then
  551. begin
  552. repeat until (getnextpacket < FPacketRecords) or (value <= RecordCount) or (FPacketRecords = -1);
  553. if value > RecordCount then
  554. begin
  555. DatabaseError(SNoSuchRecord,self);
  556. exit;
  557. end;
  558. end;
  559. TmpRecBuffer := FFirstRecBuf;
  560. for recnr := 1 to value-1 do
  561. TmpRecBuffer := TmpRecBuffer^.next;
  562. GotoBookmark(@TmpRecBuffer);
  563. end;
  564. function TBufDataset.GetRecNo: Longint;
  565. Var SearchRecBuffer : PBufRecLinkItem;
  566. TmpRecBuffer : PBufRecLinkItem;
  567. recnr : integer;
  568. abuf : PChar;
  569. begin
  570. abuf := ActiveBuffer;
  571. // If abuf isn't assigned, the recordset probably isn't opened.
  572. if assigned(abuf) and (FBRecordCount>0) and (state <> dsInsert) then
  573. begin
  574. GetBookmarkData(abuf,@SearchRecBuffer);
  575. TmpRecBuffer := FFirstRecBuf;
  576. recnr := 1;
  577. while TmpRecBuffer <> SearchRecBuffer do
  578. begin
  579. inc(recnr);
  580. TmpRecBuffer := TmpRecBuffer^.next;
  581. end;
  582. result := recnr;
  583. end
  584. else result := 0;
  585. end;
  586. function TBufDataset.IsCursorOpen: Boolean;
  587. begin
  588. Result := FOpen;
  589. end;
  590. Function TBufDataset.GetRecordCount: Longint;
  591. begin
  592. Result := FBRecordCount;
  593. end;
  594. Function TBufDataSet.UpdateStatus: TUpdateStatus;
  595. begin
  596. Result:=usUnmodified;
  597. if GetRecordUpdateBuffer then
  598. case FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind of
  599. ukModify : Result := usModified;
  600. ukInsert : Result := usInserted;
  601. ukDelete : Result := usDeleted;
  602. end;
  603. end;
  604. Function TBufDataset.Locate(const KeyFields: string; const KeyValues: Variant; options: TLocateOptions) : boolean;
  605. function CompareText0(substr, astr: pchar; len : integer; options: TLocateOptions): integer;
  606. var
  607. i : integer; Chr1, Chr2: byte;
  608. begin
  609. result := 0;
  610. i := 0;
  611. chr1 := 1;
  612. while (result=0) and (i<len) and (chr1 <> 0) do
  613. begin
  614. Chr1 := byte(substr[i]);
  615. Chr2 := byte(astr[i]);
  616. inc(i);
  617. if loCaseInsensitive in options then
  618. begin
  619. if Chr1 in [97..122] then
  620. dec(Chr1,32);
  621. if Chr2 in [97..122] then
  622. dec(Chr2,32);
  623. end;
  624. result := Chr1 - Chr2;
  625. end;
  626. if (result <> 0) and (chr1 = 0) and (loPartialKey in options) then result := 0;
  627. end;
  628. var keyfield : TField; // Field to search in
  629. ValueBuffer : pchar; // Pointer to value to search for, in TField' internal format
  630. VBLength : integer;
  631. FieldBufPos : PtrInt; // Amount to add to the record buffer to get the FieldBuffer
  632. CurrLinkItem: PBufRecLinkItem;
  633. CurrBuff : pchar;
  634. bm : TBufBookmark;
  635. CheckNull : Boolean;
  636. SaveState : TDataSetState;
  637. begin
  638. // For now it is only possible to search in one field at the same time
  639. result := False;
  640. if IsEmpty then exit;
  641. keyfield := FieldByName(keyfields);
  642. CheckNull := VarIsNull(KeyValues);
  643. if not CheckNull then
  644. begin
  645. SaveState := State;
  646. SetTempState(dsFilter);
  647. keyfield.Value := KeyValues;
  648. RestoreState(SaveState);
  649. FieldBufPos := FFieldBufPositions[keyfield.FieldNo-1];
  650. VBLength := keyfield.DataSize;
  651. ValueBuffer := AllocMem(VBLength);
  652. currbuff := pointer(FLastRecBuf)+sizeof(TBufRecLinkItem)+FieldBufPos;
  653. move(currbuff^,ValueBuffer^,VBLength);
  654. end;
  655. CurrLinkItem := FFirstRecBuf;
  656. if CheckNull then
  657. begin
  658. repeat
  659. currbuff := pointer(CurrLinkItem)+sizeof(TBufRecLinkItem);
  660. if GetFieldIsnull(pbyte(CurrBuff),keyfield.Fieldno-1) then
  661. begin
  662. result := True;
  663. break;
  664. end;
  665. CurrLinkItem := CurrLinkItem^.next;
  666. if CurrLinkItem = FLastRecBuf then getnextpacket;
  667. until CurrLinkItem = FLastRecBuf;
  668. end
  669. else if keyfield.DataType = ftString then
  670. begin
  671. repeat
  672. currbuff := pointer(CurrLinkItem)+sizeof(TBufRecLinkItem);
  673. if not GetFieldIsnull(pbyte(CurrBuff),keyfield.Fieldno-1) then
  674. begin
  675. inc(CurrBuff,FieldBufPos);
  676. if CompareText0(ValueBuffer,CurrBuff,VBLength,options) = 0 then
  677. begin
  678. result := True;
  679. break;
  680. end;
  681. end;
  682. CurrLinkItem := CurrLinkItem^.next;
  683. if CurrLinkItem = FLastRecBuf then getnextpacket;
  684. until CurrLinkItem = FLastRecBuf;
  685. end
  686. else
  687. begin
  688. repeat
  689. currbuff := pointer(CurrLinkItem)+sizeof(TBufRecLinkItem);
  690. if not GetFieldIsnull(pbyte(CurrBuff),keyfield.Fieldno-1) then
  691. begin
  692. inc(CurrBuff,FieldBufPos);
  693. if CompareByte(ValueBuffer^,CurrBuff^,VBLength) = 0 then
  694. begin
  695. result := True;
  696. break;
  697. end;
  698. end;
  699. CurrLinkItem := CurrLinkItem^.next;
  700. if CurrLinkItem = FLastRecBuf then getnextpacket;
  701. until CurrLinkItem = FLastRecBuf;
  702. end;
  703. if Result then
  704. begin
  705. bm.BookmarkData := CurrLinkItem;
  706. bm.BookmarkFlag := bfCurrent;
  707. GotoBookmark(@bm);
  708. end;
  709. ReAllocmem(ValueBuffer,0);
  710. end;