bufdataset.inc 20 KB

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