ddg_ds.pp 14 KB

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