ddg_ds.pp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. unit DDG_DS;
  2. {$define dsdebug}
  3. interface
  4. uses Db, Classes, DDG_Rec;
  5. type
  6. PInteger = ^Integer;
  7. // Bookmark information record to support TDataset bookmarks:
  8. PDDGBookmarkInfo = ^TDDGBookmarkInfo;
  9. TDDGBookmarkInfo = record
  10. BookmarkData: Integer;
  11. BookmarkFlag: TBookmarkFlag;
  12. end;
  13. // List used to maintain access to file of record:
  14. TIndexList = class(TList)
  15. public
  16. procedure LoadFromFile(const FileName: string); virtual;
  17. procedure LoadFromStream(Stream: TStream); virtual;
  18. procedure SaveToFile(const FileName: string); virtual;
  19. procedure SaveToStream(Stream: TStream); virtual;
  20. end;
  21. // Specialized DDG TDataset descendant for our "table" data:
  22. TDDGDataSet = class(TDataSet)
  23. private
  24. function GetDataFileSize: Integer;
  25. public
  26. FDataFile: TDDGDataFile;
  27. FIdxName: string;
  28. FIndexList: TIndexList;
  29. FTableName: string;
  30. FRecordPos: Integer;
  31. FRecordSize: Integer;
  32. FBufferSize: Integer;
  33. procedure SetTableName(const Value: string);
  34. protected
  35. { Mandatory overrides }
  36. // Record buffer methods:
  37. function AllocRecordBuffer: PChar; override;
  38. procedure FreeRecordBuffer(var Buffer: PChar); override;
  39. procedure InternalInitRecord(Buffer: PChar); override;
  40. function GetRecord(Buffer: PChar; GetMode: TGetMode;
  41. DoCheck: Boolean): TGetResult; override;
  42. function GetRecordSize: Word; override;
  43. procedure SetFieldData(Field: TField; Buffer: Pointer); override;
  44. // Bookmark methods:
  45. procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
  46. function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
  47. procedure InternalGotoBookmark(ABookmark: Pointer); override;
  48. procedure InternalSetToRecord(Buffer: PChar); override;
  49. procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
  50. procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
  51. // Navigational methods:
  52. procedure InternalFirst; override;
  53. procedure InternalLast; override;
  54. // Editing methods:
  55. procedure InternalAddRecord(Buffer: Pointer; DoAppend: Boolean); override;
  56. procedure InternalDelete; override;
  57. procedure InternalPost; override;
  58. // Misc methods:
  59. procedure InternalClose; override;
  60. procedure InternalHandleException; override;
  61. procedure InternalInitFieldDefs; override;
  62. procedure InternalOpen; override;
  63. function IsCursorOpen: Boolean; override;
  64. { Optional overrides }
  65. function GetRecordCount: Integer; override;
  66. function GetRecNo: Integer; override;
  67. procedure SetRecNo(Value: Integer); override;
  68. public
  69. constructor Create(AOwner: TComponent); override;
  70. destructor Destroy; override;
  71. function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
  72. // Additional procedures
  73. procedure EmptyTable;
  74. published
  75. property Active;
  76. property TableName: string read FTableName write SetTableName;
  77. property BeforeOpen;
  78. property AfterOpen;
  79. property BeforeClose;
  80. property AfterClose;
  81. property BeforeInsert;
  82. property AfterInsert;
  83. property BeforeEdit;
  84. property AfterEdit;
  85. property BeforePost;
  86. property AfterPost;
  87. property BeforeCancel;
  88. property AfterCancel;
  89. property BeforeDelete;
  90. property AfterDelete;
  91. property BeforeScroll;
  92. property AfterScroll;
  93. property OnDeleteError;
  94. property OnEditError;
  95. // Additional Properties
  96. property DataFileSize: Integer read GetDataFileSize;
  97. end;
  98. implementation
  99. uses SysUtils;
  100. const
  101. feDDGTable = '.ddg';
  102. feDDGIndex = '.ddx';
  103. // note that file is not being locked!
  104. { TIndexList }
  105. procedure TIndexList.LoadFromFile(const FileName: string);
  106. var
  107. F: TFileStream;
  108. begin
  109. F := TFileStream.Create(FileName, fmOpenRead);
  110. try
  111. LoadFromStream(F);
  112. finally
  113. F.Free;
  114. end;
  115. end;
  116. procedure TIndexList.LoadFromStream(Stream: TStream);
  117. var
  118. Value: Integer;
  119. begin
  120. while Stream.Position < Stream.Size do
  121. begin
  122. Stream.Read(Value, SizeOf(Value));
  123. Add(Pointer(Value));
  124. end;
  125. end;
  126. procedure TIndexList.SaveToFile(const FileName: string);
  127. var
  128. F: TFileStream;
  129. begin
  130. F := TFileStream.Create(FileName, fmCreate);
  131. try
  132. SaveToStream(F);
  133. finally
  134. F.Free;
  135. end;
  136. end;
  137. procedure TIndexList.SaveToStream(Stream: TStream);
  138. var
  139. i: Integer;
  140. Value: Integer;
  141. begin
  142. for i := 0 to Count - 1 do
  143. begin
  144. Value := Integer(Items[i]);
  145. Stream.Write(Value, SizeOf(Value));
  146. end;
  147. end;
  148. { TDDGDataSet }
  149. constructor TDDGDataSet.Create(AOwner: TComponent);
  150. begin
  151. FIndexList := TIndexList.Create;
  152. FRecordSize := SizeOf(TDDGData);
  153. FBufferSize := FRecordSize + SizeOf(TDDGBookmarkInfo);
  154. inherited Create(AOwner);
  155. end;
  156. destructor TDDGDataSet.Destroy;
  157. begin
  158. inherited Destroy;
  159. FIndexList.Free;
  160. end;
  161. function TDDGDataSet.AllocRecordBuffer: PChar;
  162. begin
  163. Result := AllocMem(FBufferSize);
  164. end;
  165. procedure TDDGDataSet.FreeRecordBuffer(var Buffer: PChar);
  166. begin
  167. FreeMem(Buffer);
  168. end;
  169. procedure TDDGDataSet.InternalInitRecord(Buffer: PChar);
  170. begin
  171. FillChar(Buffer^, FBufferSize, 0);
  172. end;
  173. function TDDGDataSet.GetRecord(Buffer: PChar; GetMode: TGetMode;
  174. DoCheck: Boolean): TGetResult;
  175. var
  176. IndexPos: Integer;
  177. begin
  178. if FIndexList.Count < 1 then
  179. Result := grEOF
  180. else begin
  181. Result := grOk;
  182. case GetMode of
  183. gmPrior:
  184. if FRecordPos <= 0 then
  185. begin
  186. Result := grBOF;
  187. FRecordPos := -1;
  188. end
  189. else
  190. Dec(FRecordPos);
  191. gmCurrent:
  192. if (FRecordPos < 0) or (FRecordPos >= RecordCount) then
  193. Result := grError;
  194. gmNext:
  195. if FRecordPos >= RecordCount-1 then
  196. Result := grEOF
  197. else
  198. Inc(FRecordPos);
  199. end;
  200. if Result = grOk then
  201. begin
  202. IndexPos := Integer(FIndexList[FRecordPos]);
  203. Seek(FDataFile, IndexPos);
  204. BlockRead(FDataFile, PDDGData(Buffer)^, 1);
  205. with PDDGBookmarkInfo(Buffer + FRecordSize)^ do
  206. begin
  207. BookmarkData := FRecordPos;
  208. BookmarkFlag := bfCurrent;
  209. end;
  210. end
  211. else if (Result = grError) and DoCheck then
  212. DatabaseError('No records');
  213. end;
  214. end;
  215. function TDDGDataSet.GetRecordSize: Word;
  216. begin
  217. Result := FRecordSize;
  218. end;
  219. function TDDGDataSet.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
  220. begin
  221. Result := True;
  222. case Field.Index of
  223. 0:
  224. begin
  225. Move(ActiveBuffer^, Buffer^, Field.Size);
  226. Result := PChar(Buffer)^ <> #0;
  227. end;
  228. 1: Move(PDDGData(ActiveBuffer)^.Height, Buffer^, Field.DataSize);
  229. 2: Move(PDDGData(ActiveBuffer)^.LongField, Buffer^, Field.DataSize);
  230. 3: Move(PDDGData(ActiveBuffer)^.ShoeSize, Buffer^, Field.DataSize);
  231. 4: Move(PDDGData(ActiveBuffer)^.WordField, Buffer^, Field.DataSize);
  232. 5: Move(PDDGData(ActiveBuffer)^.DateTimeField, Buffer^, Field.DataSize);
  233. 6: Move(PDDGData(ActiveBuffer)^.TimeField, Buffer^, Field.DataSize);
  234. 7: Move(PDDGData(ActiveBuffer)^.DateField, Buffer^, Field.DataSize);
  235. 8: Move(PDDGData(ActiveBuffer)^.Even, Buffer^, Field.DataSize);
  236. end;
  237. end;
  238. procedure TDDGDataSet.SetFieldData(Field: TField; Buffer: Pointer);
  239. begin
  240. case Field.Index of
  241. 0: Move(Buffer^, ActiveBuffer^, Field.Size);
  242. 1: Move(Buffer^, PDDGData(ActiveBuffer)^.Height, Field.DataSize);
  243. 2: Move(Buffer^, PDDGData(ActiveBuffer)^.LongField, Field.DataSize);
  244. 3: Move(Buffer^, PDDGData(ActiveBuffer)^.ShoeSize, Field.DataSize);
  245. 4: Move(Buffer^, PDDGData(ActiveBuffer)^.WordField, Field.DataSize);
  246. 5: Move(Buffer^, PDDGData(ActiveBuffer)^.DateTimeField, Field.DataSize);
  247. 6: Move(Buffer^, PDDGData(ActiveBuffer)^.TimeField, Field.DataSize);
  248. 7: Move(Buffer^, PDDGData(ActiveBuffer)^.DateField, Field.DataSize);
  249. 8: Move(Buffer^, PDDGData(ActiveBuffer)^.Even, Field.DataSize);
  250. end;
  251. DataEvent(deFieldChange, Longint(Field));
  252. end;
  253. procedure TDDGDataSet.GetBookmarkData(Buffer: PChar; Data: Pointer);
  254. begin
  255. PInteger(Data)^ := PDDGBookmarkInfo(Buffer + FRecordSize)^.BookmarkData;
  256. end;
  257. function TDDGDataSet.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
  258. begin
  259. Result := PDDGBookmarkInfo(Buffer + FRecordSize)^.BookmarkFlag;
  260. end;
  261. procedure TDDGDataSet.InternalGotoBookmark(ABookmark: Pointer);
  262. begin
  263. FRecordPos := PInteger(ABookmark)^;
  264. Writeln ('Bookmark : Setting record position to : ',FrecordPos);
  265. end;
  266. procedure TDDGDataSet.InternalSetToRecord(Buffer: PChar);
  267. begin
  268. // bookmark value is the same as an offset into the file
  269. FRecordPos := PDDGBookmarkInfo(Buffer + FRecordSize)^.Bookmarkdata;
  270. end;
  271. procedure TDDGDataSet.SetBookmarkData(Buffer: PChar; Data: Pointer);
  272. begin
  273. PDDGBookmarkInfo(Buffer + FRecordSize)^.BookmarkData := PInteger(Data)^;
  274. end;
  275. procedure TDDGDataSet.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
  276. begin
  277. PDDGBookmarkInfo(Buffer + FRecordSize)^.BookmarkFlag := Value;
  278. end;
  279. procedure TDDGDataSet.InternalFirst;
  280. begin
  281. FRecordPos := -1;
  282. end;
  283. procedure TDDGDataSet.InternalInitFieldDefs;
  284. begin
  285. // create FieldDefs which map to each field in the data record
  286. FieldDefs.Clear;
  287. TFieldDef.Create(FieldDefs, 'Name', ftString, SizeOf(TNameStr), False, 1);
  288. TFieldDef.Create(FieldDefs, 'Height', ftFloat, 0, False, 2);
  289. TFieldDef.Create(FieldDefs, 'LongField',ftInteger, 0, False, 3);
  290. TFieldDef.Create(FieldDefs, 'ShoeSize', ftSmallint, 0, False, 4);
  291. TFieldDef.Create(FieldDefs, 'WordField', ftword, 0, false, 5);
  292. TFieldDef.Create(FieldDefs, 'DateTimeField', ftDateTime, 0, false, 6);
  293. TFieldDef.Create(FieldDefs, 'TimeField',ftTime, 0, false, 7);
  294. TFieldDef.Create(FieldDefs, 'DateField',ftDate, 0, false, 8);
  295. TFieldDef.Create(FieldDefs, 'Booleanfield',ftboolean, 0, False, 9);
  296. end;
  297. procedure TDDGDataSet.InternalLast;
  298. begin
  299. FRecordPos := FIndexList.Count;
  300. end;
  301. procedure TDDGDataSet.InternalClose;
  302. begin
  303. if FileRec(FDataFile).Mode <> 0 then
  304. CloseFile(FDataFile);
  305. FIndexList.SaveToFile(FIdxName);
  306. FIndexList.Clear;
  307. if DefaultFields then
  308. DestroyFields;
  309. FRecordPos := -1;
  310. FillChar(FDataFile, SizeOf(FDataFile), 0);
  311. end;
  312. procedure TDDGDataSet.InternalHandleException;
  313. begin
  314. // standard implementation for this method:
  315. // Application.HandleException(Self);
  316. end;
  317. procedure TDDGDataSet.InternalDelete;
  318. begin
  319. FIndexList.Delete(FRecordPos);
  320. if FRecordPos >= FIndexList.Count then Dec(FRecordPos);
  321. end;
  322. procedure TDDGDataSet.InternalAddRecord(Buffer: Pointer; DoAppend: Boolean);
  323. var
  324. RecPos: Integer;
  325. begin
  326. Seek(FDataFile, FileSize(FDataFile));
  327. BlockWrite(FDataFile, PDDGData(Buffer)^, 1);
  328. if DoAppend then
  329. begin
  330. FIndexList.Add(Pointer(FileSize(FDataFile) - 1));
  331. InternalLast;
  332. end
  333. else begin
  334. if FRecordPos = -1 then RecPos := 0
  335. else RecPos := FRecordPos;
  336. FIndexList.Insert(RecPos, Pointer(FileSize(FDataFile) - 1));
  337. end;
  338. FIndexList.SaveToFile(FIdxName);
  339. end;
  340. procedure TDDGDataSet.InternalOpen;
  341. var
  342. HFile: THandle;
  343. begin
  344. // make sure table and index files exist
  345. FIdxName := ChangeFileExt(FTableName, feDDGIndex);
  346. if not (FileExists(FTableName) and FileExists(FIdxName)) then
  347. begin
  348. {
  349. if MessageDlg('Table or index file not found. Create new table?',
  350. mtConfirmation, [mbYes, mbNo], 0) = mrYes then
  351. begin
  352. HFile := FileCreate(FTableName);
  353. if HFile = -1 then
  354. DatabaseError('Error creating table file');
  355. FileClose(HFile);
  356. HFile := FileCreate(FIdxName);
  357. if HFile = -1 then
  358. DatabaseError('Error creating index file');
  359. FileClose(HFile);
  360. end
  361. else
  362. }
  363. DatabaseError('Could not open table');
  364. end;
  365. // open data file
  366. FileMode := fmOpenReadWrite;
  367. Writeln ('OPening data file');
  368. AssignFile(FDataFile, FTableName);
  369. Reset(FDataFile);
  370. try
  371. writeln ('Loading index file');
  372. FIndexList.LoadFromFile(FIdxName); // initialize index TList from file
  373. FRecordPos := -1; // initial record pos before BOF
  374. BookmarkSize := SizeOf(Integer); // initialize bookmark size for VCL
  375. InternalInitFieldDefs; // initialize FieldDef objects
  376. // Create TField components when no persistent fields have been created
  377. {$ifdef dsdebug}
  378. writeln ('Creating Fields');
  379. {$endif}
  380. if DefaultFields then CreateFields;
  381. {$ifdef dsdebug}
  382. writeln ('Binding Fields');
  383. {$endif}
  384. BindFields(True); // bind FieldDefs to actual data
  385. except
  386. {$ifdef dsdebug}
  387. Writeln ('Caught Exception !!');
  388. {$endif}
  389. CloseFile(FDataFile);
  390. FillChar(FDataFile, SizeOf(FDataFile), 0);
  391. raise;
  392. end;
  393. {$ifdef dsdebug}
  394. Writeln ('End of internalopen');
  395. {$endif}
  396. end;
  397. procedure TDDGDataSet.InternalPost;
  398. var
  399. RecPos, InsPos: Integer;
  400. begin
  401. {$ifdef dsdebug}
  402. Writeln ('Starting internal post.');
  403. {$endif}
  404. if FRecordPos = -1 then
  405. RecPos := 0
  406. else begin
  407. if State = dsEdit then RecPos := Integer(FIndexList[FRecordPos])
  408. else RecPos := FileSize(FDataFile);
  409. end;
  410. Seek(FDataFile, RecPos);
  411. {$ifdef dsdebug}
  412. Writeln ('Writing record to disk.');
  413. {$endif}
  414. BlockWrite(FDataFile, PDDGData(ActiveBuffer)^, 1);
  415. if State <> dsEdit then
  416. begin
  417. if FRecordPos = -1 then InsPos := 0
  418. else InsPos := FRecordPos;
  419. FIndexList.Insert(InsPos, Pointer(RecPos));
  420. end;
  421. {$ifdef dsdebug}
  422. Writeln ('Writing index to disk.');
  423. {$endif}
  424. FIndexList.SaveToFile(FIdxName);
  425. end;
  426. function TDDGDataSet.IsCursorOpen: Boolean;
  427. begin
  428. Result := FileRec(FDataFile).Mode <> 0;
  429. end;
  430. function TDDGDataSet.GetRecordCount: Integer;
  431. begin
  432. Result := FIndexList.Count;
  433. end;
  434. function TDDGDataSet.GetRecNo: Integer;
  435. begin
  436. UpdateCursorPos;
  437. if (FRecordPos = -1) and (RecordCount > 0) then
  438. Result := 1
  439. else
  440. Result := FRecordPos + 1;
  441. end;
  442. procedure TDDGDataSet.SetRecNo(Value: Integer);
  443. begin
  444. if (Value >= 0) and (Value <= FIndexList.Count-1) then
  445. begin
  446. FRecordPos := Value - 1;
  447. Resync([]);
  448. end;
  449. end;
  450. procedure TDDGDataSet.SetTableName(const Value: string);
  451. begin
  452. CheckInactive;
  453. FTableName := Value;
  454. if ExtractFileExt(FTableName) = '' then
  455. FTableName := FTableName + feDDGTable;
  456. FIdxName := ChangeFileExt(FTableName, feDDGIndex);
  457. end;
  458. function TDDGDataSet.GetDataFileSize: Integer;
  459. begin
  460. Result := FileSize(FDataFile);
  461. end;
  462. procedure TDDGDataSet.EmptyTable;
  463. var
  464. HFile: THandle;
  465. begin
  466. Close;
  467. DeleteFile(FTableName);
  468. HFile := FileCreate(FTableName);
  469. FileClose(HFile);
  470. DeleteFile(FIdxName);
  471. HFile := FileCreate(FIdxName);
  472. FileClose(HFile);
  473. Open;
  474. end;
  475. end.
  476. $Log$
  477. Revision 1.3 2002-09-07 15:15:23 peter
  478. * old logs removed and tabs fixed
  479. }