Dataset.txt 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. Contents
  2. ========
  3. + General remarks
  4. + Fields system
  5. + The buffers
  6. + Dataset implementation
  7. + Switchable datasets
  8. ===============
  9. General remarks
  10. ===============
  11. - All fields and descendents implemented.
  12. - No calculated fields.
  13. - No persistent fields; this must be added later.
  14. =============
  15. Fields system
  16. =============
  17. Buffers are completely handled by the Dataset. Fields don't handle
  18. their own buffers. The FValueBuffer of the field is only used during
  19. validation.
  20. This allows the dataset to allocate a number of buffers for the current
  21. record and the N next records. (getnextrecords/getpriorrecords method)
  22. This means that all field mechanisms MUST pass through GetData/SetData,
  23. since FValueBuffer is only valid during validation.
  24. ===========
  25. The Buffers
  26. ===========
  27. A buffer contains all the data for 1 record of the dataset, and also
  28. the bookmark information. Bookmark information is REQUIRED.
  29. The dataset allocates by default 'DefaultBufferCount+1' records(buffers)
  30. This constant can be changed at the beginning of dataset.inc, e.g.
  31. if you know you'll be working with big datasets, you can
  32. increase this constant.
  33. The buffers are stored as pchars in the FBuffers array;
  34. The following constants are userd when handling this array:
  35. FBufferCount : The number of buffers allocated, minus one.
  36. FRecordCount : The number of buffers that is actually filled in.
  37. FActiveRecord : The index of the active record in TDataset.
  38. FCurrentRecord : The index of the supposedly active record in the underlying
  39. dataset (ie. the index in the last call to SetToInternalRecord)
  40. Call CursorPosChanged to reset FCurrentRecord if the active
  41. record in the underlaying dataset has changed.
  42. So the following picture follows from this:
  43. +---------------+
  44. | 0 |
  45. +---------------+
  46. | 1 |
  47. +---------------+
  48. | |
  49. ...
  50. | |
  51. +---------------+
  52. | FActiveRecord |
  53. +---------------+
  54. | |
  55. ...
  56. | |
  57. +---------------+
  58. |FRecordCount-1 |
  59. +---------------+
  60. | |
  61. ...
  62. | |
  63. +---------------+
  64. | FBufferCount |
  65. +---------------+
  66. The array is zero based.
  67. The following methods are used to manipulate the array:
  68. GetNextRecords: tries to fill up the entire array, going forward
  69. GetPriorRecords: tries to fill up the entire array, going backward
  70. GetNextRecord: gets the next record. Shifts the array if FRecordCount=BufferCount-1
  71. GetPriorRecord: gets the previous record. Shifts the array if FRecordCount=BufferCount-1
  72. For the last 2 methods: the underlying record pointer must be on the
  73. last/first record in the dataset, or things will go wrong.
  74. resync tries to refresh the array from the underlying dataset; it uses the
  75. bookmarks for that.
  76. =======================
  77. Dataset implementations
  78. =======================
  79. TDataset does most of the work associated with fields, buffers and
  80. navigating/editing/adding/removing records of some source of data.
  81. There are, however, some methods that need to be filled in so that
  82. a real TDataset can be implemented.
  83. In order to have a working Dataset, the following Methods need to be
  84. overridden in order to make a dataset descendant:
  85. function AllocRecordBuffer: PChar; virtual; abstract;
  86. -----------------------------------------------------
  87. Must allocate enough memory to store a complete record in the dataset.
  88. Optionally, this buffer must contain enough memory to store bookmark data.
  89. The descendent must be able to construct a bookmark from this buffer.
  90. procedure FreeRecordBuffer(var Buffer: PChar); virtual; abstract;
  91. -----------------------------------------------------------------
  92. Must free the memory allocated in the AllocRecordBuffer call.
  93. procedure GetBookmarkData(Buffer: PChar; Data: Pointer); virtual; abstract;
  94. ---------------------------------------------------------------------------
  95. Puts the bookmark data for Buffer into the area pointed to by Data.
  96. function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; virtual; abstract;
  97. --------------------------------------------------------------------------
  98. Returns the bookmarkflag associated with Buffer.
  99. function GetFieldData(Field: TField; Buffer: Pointer): Boolean; virtual; abstract;
  100. ----------------------------------------------------------------------------------
  101. Puts the data for field Field from the active buffer into Buffer.
  102. This is called whenever a field value is demanded, so it must be
  103. efficient.
  104. function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; virtual; abstract;
  105. -----------------------------------------------------------------------------------
  106. This method must do 3 things:
  107. 1) Get the record data for the next/current/previous record, depending
  108. on the GetMode value. It should return
  109. grOK if all was OK.
  110. grBOF if the previous record was requested, and we are at the start.
  111. grEOF if the next record was requested, and we are at the end.
  112. grError if an error occurred.
  113. 2) If DoCheck is True, and the result is grError, then an exception must be
  114. raised.
  115. 3) It should initialize bookmark data for this record with flag 'bfCurrent'
  116. This data can be stored in the buffer, if space was allocated for it with
  117. AllocRecordBuffer.
  118. function GetRecordSize: Word; virtual; abstract;
  119. ------------------------------------------------
  120. Should return the record size - this includes ONLY the data portion
  121. of the buffer. It excludes any bookmark or housekeeping info you may
  122. have put in the buffer.
  123. procedure InternalAddRecord(Buffer: Pointer; Append: Boolean); virtual; abstract;
  124. ---------------------------------------------------------------------------------
  125. Adds a record to the dataset. The record's data is in Buffer and Append
  126. indicates whether the record should be appended (True) or Inserted (False).
  127. Note that for SQL based datasets, this has no meaning.
  128. procedure InternalClose; virtual; abstract;
  129. -------------------------------------------
  130. Closes the dataset. Any resources allocated in InternalOpen should be freed
  131. here.
  132. procedure InternalDelete; virtual; abstract;
  133. --------------------------------------------
  134. Deletes the current Record.
  135. procedure InternalFirst; virtual; abstract;
  136. -------------------------------------------
  137. This is called when 'First' is called. After this method, getrecord
  138. should return 'grBOF' if the previous record is requested, and it should
  139. return the next record if the next record is requested.
  140. procedure InternalGotoBookmark(ABookmark: Pointer); virtual; abstract;
  141. ----------------------------------------------------------------------
  142. Set the record position on the position that is associated with the
  143. ABookMark data. The ABookMark data is the data that is acquired through
  144. the GetBookMarkData call, and should be kept for each record.
  145. procedure InternalHandleException; virtual; abstract;
  146. -----------------------------------------------------
  147. This procedure can try to handle exceptions raised in the Dataset, and
  148. raise an exception if it cannot handle it.
  149. If not used, just implement an empty call (as is currently done with
  150. most datasets).
  151. procedure InternalInitFieldDefs; virtual; abstract;
  152. ---------------------------------------------------
  153. This method should be called from InternalOpen, and should
  154. initialize FieldDef definitions for all fields in a record.
  155. It should add these definitions to the FFielddefs object.
  156. procedure InternalInitRecord(Buffer: PChar); virtual; abstract;
  157. ---------------------------------------------------------------
  158. This method is called to initialize a field buffer when the dataset
  159. is put into edit or append mode. Mostly, you'll want to zero out the
  160. buffer.
  161. procedure InternalLast; virtual; abstract;
  162. ------------------------------------------
  163. This is called when 'Last' is called. After this method, getrecord
  164. should return 'grEOF' if the next record is requested, and it should
  165. return the last record if the previous record is requested.
  166. procedure InternalOpen; virtual; abstract;
  167. ------------------------------------------
  168. Open the dataset. You must call internalinitfielddefs.
  169. If DefaultFields is True, then you must call CreateFields,
  170. which will create the necessary TFields from the fielddefs.
  171. procedure InternalPost; virtual; abstract;
  172. ------------------------------------------
  173. Post the data in the active buffer to the underlying dataset.
  174. procedure InternalSetToRecord(Buffer: PChar); virtual; abstract;
  175. ----------------------------------------------------------------
  176. Set the current record to the record in Buffer; if bookmark data
  177. is specified in this buffer, that data can be used to determine which
  178. record this should be.
  179. function IsCursorOpen: Boolean; virtual; abstract;
  180. --------------------------------------------------
  181. This function should return True if data is available, even if the dataset
  182. is not active.
  183. procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); virtual; abstract;
  184. ----------------------------------------------------------------------------------
  185. Set the bookmarkflag 'Value' on the data in Buffer.
  186. procedure SetBookmarkData(Buffer: PChar; Data: Pointer); virtual; abstract;
  187. ---------------------------------------------------------------------------
  188. Move the bookmark data in 'Data' to the bookmark data associated with Buffer
  189. procedure SetFieldData(Field: TField; Buffer: Pointer); virtual; abstract;
  190. --------------------------------------------------------------------------
  191. Move the data associated with Field from Buffer to the active record buffer.
  192. This procedure is called after inserting/editing field data.
  193. ===================
  194. Switchable datasets
  195. ===================
  196. In order to have flexible database access, the concept of TDatabase and
  197. TDBDataset is introduced. The idea is that, in a visual IDE, the change
  198. from one database to another is achieved by simply removing one TDatabase
  199. descendent (say, TMySqlDatabase) with another (say, TPostGreSQLDatabase)
  200. and that the Datasets remain untouched.
  201. In order to make this possible, the following scheme is used:
  202. when a TDBdataset descendant is put on Active, it requests a TRecordSet
  203. from the TDatabase. The TRecordSet is an abstract object that should be
  204. implemented together with each database. The TDBDataset then uses the
  205. TRecordSet to navigate through the records and edit/add/modify them.
  206. The TDBdataset implements the abstract methods of Tdataset in order to
  207. achieve this.
  208. There will be 2 descendants of TDBdataset: TTable and TQuery; both will
  209. implement the final abstract methods of TDataset in order to achieve a
  210. complete TDataset implementation.
  211. TDBDataset implements most of the initialization of fields, so the
  212. implementation of TRecordSet will be as bare bones as possible.
  213. What is needed:
  214. ---------------
  215. Some properties describing the data:
  216. FieldCount : Number of fields in a record.
  217. FieldTypes[Index] : Types of the fields (TFieldType). Zero based.
  218. FieldNames[Index] : Names of the fields. Zero based.
  219. FieldSizes[index] : Size of the fields. Zero based.
  220. BookmarkSize : Size of a bookmark.
  221. Some properties with the data content:
  222. FieldBuffers[Index] : Buffers containing the actual data of the current record.
  223. (Nil if the field is empty)
  224. This data should be of size indicated in FieldSizes, and
  225. in a format that matches the fieldtype.
  226. BookMarkBuffer : Buffer with the current bookmark.
  227. Some methods
  228. ------------
  229. OpenRecordSet : Opens the recordset. It should initialize the FieldCount
  230. and FieldTypes, FieldNames, and FieldSizes array data.
  231. CloseRecordSet : Do whatever is needed to close the recordset.
  232. GotoBookMark : go to the record described by the bookmark. Returns true
  233. if successful, False if not.
  234. Next : Go to the next record. Returns true or false.
  235. Prior : Go to the previous record. Returns true or false.
  236. First : Go to the first record. Returns true or false.
  237. Last : Go to the last record. Returns true or false.
  238. AppendBuffer : Append a buffer to the records.