dbf_fields.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. unit dbf_fields;
  2. interface
  3. {$I Dbf_Common.inc}
  4. uses
  5. Classes,
  6. SysUtils,
  7. Db,
  8. Dbf_Common,
  9. Dbf_Str;
  10. type
  11. PDbfFieldDef = ^TDbfFieldDef;
  12. TDbfFieldDef = class(TCollectionItem)
  13. private
  14. FFieldName: string;
  15. FFieldType: TFieldType;
  16. FNativeFieldType: TDbfFieldType;
  17. FDefaultBuf: PChar;
  18. FMinBuf: PChar;
  19. FMaxBuf: PChar;
  20. FSize: Integer;
  21. FPrecision: Integer;
  22. FHasDefault: Boolean;
  23. FHasMin: Boolean;
  24. FHasMax: Boolean;
  25. FAllocSize: Integer;
  26. FCopyFrom: Integer;
  27. FOffset: Integer;
  28. FAutoInc: Cardinal;
  29. FRequired: Boolean;
  30. FIsLockField: Boolean;
  31. FNullPosition: integer;
  32. function GetDbfVersion: TXBaseVersion;
  33. procedure SetNativeFieldType(lFieldType: TDbfFieldType);
  34. procedure SetFieldType(lFieldType: TFieldType);
  35. procedure SetSize(lSize: Integer);
  36. procedure SetPrecision(lPrecision: Integer);
  37. procedure VCLToNative;
  38. procedure NativeToVCL;
  39. procedure FreeBuffers;
  40. protected
  41. function GetDisplayName: string; override;
  42. procedure AssignTo(Dest: TPersistent); override;
  43. property DbfVersion: TXBaseVersion read GetDbfVersion;
  44. public
  45. constructor Create(Collection: TCollection); override;
  46. destructor Destroy; override;
  47. procedure Assign(Source: TPersistent); override;
  48. procedure AssignDb(DbSource: TFieldDef);
  49. procedure CheckSizePrecision;
  50. procedure SetDefaultSize;
  51. procedure AllocBuffers;
  52. function IsBlob: Boolean;
  53. property DefaultBuf: PChar read FDefaultBuf;
  54. property MinBuf: PChar read FMinBuf;
  55. property MaxBuf: PChar read FMaxBuf;
  56. property HasDefault: Boolean read FHasDefault write FHasDefault;
  57. property HasMin: Boolean read FHasMin write FHasMin;
  58. property HasMax: Boolean read FHasMax write FHasMax;
  59. property Offset: Integer read FOffset write FOffset;
  60. property AutoInc: Cardinal read FAutoInc write FAutoInc;
  61. property IsLockField: Boolean read FIsLockField write FIsLockField;
  62. property CopyFrom: Integer read FCopyFrom write FCopyFrom;
  63. published
  64. property FieldName: string read FFieldName write FFieldName;
  65. property FieldType: TFieldType read FFieldType write SetFieldType;
  66. property NativeFieldType: TDbfFieldType read FNativeFieldType write SetNativeFieldType;
  67. property NullPosition: integer read FNullPosition write FNullPosition;
  68. property Size: Integer read FSize write SetSize;
  69. property Precision: Integer read FPrecision write SetPrecision;
  70. property Required: Boolean read FRequired write FRequired;
  71. end;
  72. TDbfFieldDefs = class(TCollection)
  73. private
  74. FOwner: TPersistent;
  75. FDbfVersion: TXBaseVersion;
  76. FUseFloatFields: Boolean;
  77. function GetItem(Idx: Integer): TDbfFieldDef;
  78. protected
  79. function GetOwner: TPersistent; override;
  80. public
  81. constructor Create(Owner: TPersistent);
  82. {$ifdef SUPPORT_DEFAULT_PARAMS}
  83. procedure Add(const Name: string; DataType: TFieldType; Size: Integer = 0; Required: Boolean = False);
  84. {$else}
  85. procedure Add(const Name: string; DataType: TFieldType; Size: Integer; Required: Boolean);
  86. {$endif}
  87. function AddFieldDef: TDbfFieldDef;
  88. property Items[Idx: Integer]: TDbfFieldDef read GetItem;
  89. property DbfVersion: TXBaseVersion read FDbfVersion write FDbfVersion;
  90. property UseFloatFields: Boolean read FUseFloatFields write FUseFloatFields;
  91. end;
  92. implementation
  93. uses
  94. Dbf_DbfFile; // for dbf header structures
  95. {$I Dbf_Struct.inc}
  96. // I keep changing that fields...
  97. // Last time has been asked by Venelin Georgiev
  98. // Is he going to be the last ?
  99. const
  100. (*
  101. The theory until now was :
  102. ftSmallint 16 bits = -32768 to 32767
  103. 123456 = 6 digit max theorically
  104. DIGITS_SMALLINT = 6;
  105. ftInteger 32 bits = -2147483648 to 2147483647
  106. 12345678901 = 11 digits max
  107. DIGITS_INTEGER = 11;
  108. ftLargeInt 64 bits = -9223372036854775808 to 9223372036854775807
  109. 12345678901234567890 = 20 digits max
  110. DIGITS_LARGEINT = 20;
  111. But in fact if I accept 6 digits into a ftSmallInt then tDbf will not
  112. being able to handles fields with 999999 (6 digits).
  113. So I now oversize the field type in order to accept anithing coming from the
  114. database.
  115. ftSmallint 16 bits = -32768 to 32767
  116. -999 to 9999
  117. 4 digits max theorically
  118. DIGITS_SMALLINT = 4;
  119. ftInteger 32 bits = -2147483648 to 2147483647
  120. -99999999 to 999999999 12345678901 = 11 digits max
  121. DIGITS_INTEGER = 9;
  122. ftLargeInt 64 bits = -9223372036854775808 to 9223372036854775807
  123. -99999999999999999 to 999999999999999999
  124. DIGITS_LARGEINT = 18;
  125. *)
  126. DIGITS_SMALLINT = 4;
  127. DIGITS_INTEGER = 9;
  128. DIGITS_LARGEINT = 18;
  129. //====================================================================
  130. // DbfFieldDefs
  131. //====================================================================
  132. function TDbfFieldDefs.GetItem(Idx: Integer): TDbfFieldDef;
  133. begin
  134. Result := TDbfFieldDef(inherited GetItem(Idx));
  135. end;
  136. constructor TDbfFieldDefs.Create(Owner: TPersistent);
  137. begin
  138. inherited Create(TDbfFieldDef);
  139. FOwner := Owner;
  140. end;
  141. function TDbfFieldDefs.AddFieldDef: TDbfFieldDef;
  142. begin
  143. Result := TDbfFieldDef(inherited Add);
  144. end;
  145. function TDbfFieldDefs.GetOwner: TPersistent; {override;}
  146. begin
  147. Result := FOwner;
  148. end;
  149. procedure TDbfFieldDefs.Add(const Name: string; DataType: TFieldType; Size: Integer; Required: Boolean);
  150. var
  151. FieldDef: TDbfFieldDef;
  152. begin
  153. FieldDef := AddFieldDef;
  154. FieldDef.FieldName := Name;
  155. FieldDef.FieldType := DataType;
  156. FieldDef.Size := size;
  157. FieldDef.Required := Required;
  158. end;
  159. //====================================================================
  160. // DbfFieldDef
  161. //====================================================================
  162. constructor TDbfFieldDef.Create(Collection: TCollection); {virtual}
  163. begin
  164. inherited;
  165. FDefaultBuf := nil;
  166. FMinBuf := nil;
  167. FMaxBuf := nil;
  168. FAllocSize := 0;
  169. FCopyFrom := -1;
  170. FPrecision := 0;
  171. FHasDefault := false;
  172. FHasMin := false;
  173. FHasMax := false;
  174. FNullPosition := -1;
  175. end;
  176. destructor TDbfFieldDef.Destroy; {override}
  177. begin
  178. FreeBuffers;
  179. inherited;
  180. end;
  181. procedure TDbfFieldDef.Assign(Source: TPersistent);
  182. var
  183. DbfSource: TDbfFieldDef;
  184. begin
  185. if Source is TDbfFieldDef then
  186. begin
  187. // copy from another TDbfFieldDef
  188. DbfSource := TDbfFieldDef(Source);
  189. FFieldName := DbfSource.FieldName;
  190. FFieldType := DbfSource.FieldType;
  191. FNativeFieldType := DbfSource.NativeFieldType;
  192. FSize := DbfSource.Size;
  193. FPrecision := DbfSource.Precision;
  194. FRequired := DbfSource.Required;
  195. FCopyFrom := DbfSource.Index;
  196. FIsLockField := DbfSource.IsLockField;
  197. FNullPosition := DbfSource.NullPosition;
  198. // copy default,min,max
  199. AllocBuffers;
  200. if DbfSource.DefaultBuf <> nil then
  201. Move(DbfSource.DefaultBuf^, FDefaultBuf^, FAllocSize*3);
  202. FHasDefault := DbfSource.HasDefault;
  203. FHasMin := DbfSource.HasMin;
  204. FHasMax := DbfSource.HasMax;
  205. // do we need offsets?
  206. FOffset := DbfSource.Offset;
  207. FAutoInc := DbfSource.AutoInc;
  208. {$ifdef SUPPORT_FIELDDEF_TPERSISTENT}
  209. end else if Source is TFieldDef then begin
  210. AssignDb(TFieldDef(Source));
  211. {$endif}
  212. end else
  213. inherited Assign(Source);
  214. end;
  215. procedure TDbfFieldDef.AssignDb(DbSource: TFieldDef);
  216. begin
  217. // copy from Db.TFieldDef
  218. FFieldName := DbSource.Name;
  219. FFieldType := DbSource.DataType;
  220. FSize := DbSource.Size;
  221. FPrecision := DbSource.Precision;
  222. FRequired := DbSource.Required;
  223. {$ifdef SUPPORT_FIELDDEF_INDEX}
  224. FCopyFrom := DbSource.Index;
  225. {$endif}
  226. FIsLockField := false;
  227. // convert VCL fieldtypes to native DBF fieldtypes
  228. VCLToNative;
  229. // for integer / float fields try fill in size/precision
  230. SetDefaultSize;
  231. // VCL does not have default value support
  232. AllocBuffers;
  233. FHasDefault := false;
  234. FHasMin := false;
  235. FHasMax := false;
  236. FOffset := 0;
  237. FAutoInc := 0;
  238. end;
  239. procedure TDbfFieldDef.AssignTo(Dest: TPersistent);
  240. var
  241. DbDest: TFieldDef;
  242. begin
  243. {$ifdef SUPPORT_FIELDDEF_TPERSISTENT}
  244. // copy to VCL fielddef?
  245. if Dest is TFieldDef then
  246. begin
  247. DbDest := TFieldDef(Dest);
  248. // VCL TFieldDef does not know how to handle TDbfFieldDef!
  249. // what a shame :-)
  250. {$ifdef SUPPORT_FIELDDEF_ATTRIBUTES}
  251. DbDest.Attributes := [];
  252. DbDest.ChildDefs.Clear;
  253. DbDest.DataType := FFieldType;
  254. DbDest.Required := FRequired;
  255. DbDest.Size := FSize;
  256. DbDest.Name := FFieldName;
  257. {$endif}
  258. end else
  259. {$endif}
  260. inherited AssignTo(Dest);
  261. end;
  262. function TDbfFieldDef.GetDbfVersion: TXBaseVersion;
  263. begin
  264. Result := TDbfFieldDefs(Collection).DbfVersion;
  265. end;
  266. procedure TDbfFieldDef.SetFieldType(lFieldType: tFieldType);
  267. begin
  268. FFieldType := lFieldType;
  269. VCLToNative;
  270. SetDefaultSize;
  271. end;
  272. procedure TDbfFieldDef.SetNativeFieldType(lFieldType: tDbfFieldType);
  273. begin
  274. // get uppercase field type
  275. if (lFieldType >= 'a') and (lFieldType <= 'z') then
  276. lFieldType := Chr(Ord(lFieldType)-32);
  277. FNativeFieldType := lFieldType;
  278. NativeToVCL;
  279. CheckSizePrecision;
  280. end;
  281. procedure TDbfFieldDef.SetSize(lSize: Integer);
  282. begin
  283. FSize := lSize;
  284. CheckSizePrecision;
  285. end;
  286. procedure TDbfFieldDef.SetPrecision(lPrecision: Integer);
  287. begin
  288. FPrecision := lPrecision;
  289. CheckSizePrecision;
  290. end;
  291. procedure TDbfFieldDef.NativeToVCL;
  292. begin
  293. case FNativeFieldType of
  294. // OH 2000-11-15 dBase7 support.
  295. // Add the new fieldtypes
  296. '+' : FFieldType := ftAutoInc;
  297. 'I' : FFieldType := ftInteger;
  298. 'O' : FFieldType := ftFloat;
  299. '@', 'T':
  300. FFieldType := ftDateTime;
  301. 'C',
  302. #$91 {Russian 'C'}
  303. : FFieldType := ftString;
  304. 'L' : FFieldType := ftBoolean;
  305. 'F', 'N':
  306. begin
  307. if (FPrecision = 0) then
  308. begin
  309. if FSize <= DIGITS_SMALLINT then
  310. FFieldType := ftSmallInt
  311. else
  312. if TDbfFieldDefs(Collection).UseFloatFields then
  313. FFieldType := ftFloat
  314. else
  315. {$ifdef SUPPORT_INT64}
  316. if FSize <= DIGITS_INTEGER then
  317. FFieldType := ftInteger
  318. else
  319. FFieldType := ftLargeInt;
  320. {$else}
  321. FFieldType := ftInteger;
  322. {$endif}
  323. end else begin
  324. FFieldType := ftFloat;
  325. end;
  326. end;
  327. 'D' : FFieldType := ftDate;
  328. 'M' : FFieldType := ftMemo;
  329. 'B' : FFieldType := ftBlob;
  330. 'G' : FFieldType := ftDBaseOle;
  331. 'Y' :
  332. if DbfGlobals.CurrencyAsBCD then
  333. FFieldType := ftBCD
  334. else
  335. FFieldType := ftCurrency;
  336. '0' : FFieldType := ftBytes; { Visual FoxPro ``_NullFlags'' }
  337. else
  338. FNativeFieldType := #0;
  339. FFieldType := ftUnknown;
  340. end; //case
  341. end;
  342. procedure TDbfFieldDef.VCLToNative;
  343. begin
  344. FNativeFieldType := #0;
  345. case FFieldType of
  346. ftAutoInc : FNativeFieldType := '+';
  347. ftDateTime :
  348. if DbfVersion = xBaseVII then
  349. FNativeFieldType := '@'
  350. else
  351. if DbfVersion = xFoxPro then
  352. FNativeFieldType := 'T';
  353. {$ifdef SUPPORT_FIELDTYPES_V4}
  354. ftFixedChar,
  355. ftWideString,
  356. {$endif}
  357. ftString : FNativeFieldType := 'C';
  358. ftBoolean : FNativeFieldType := 'L';
  359. ftFloat, ftSmallInt, ftWord
  360. {$ifdef SUPPORT_INT64}
  361. , ftLargeInt
  362. {$endif}
  363. : FNativeFieldType := 'N';
  364. ftDate : FNativeFieldType := 'D';
  365. ftMemo : FNativeFieldType := 'M';
  366. ftBlob : FNativeFieldType := 'B';
  367. ftDBaseOle : FNativeFieldType := 'G';
  368. ftInteger :
  369. if DbfVersion = xBaseVII then
  370. FNativeFieldType := 'I'
  371. else
  372. FNativeFieldType := 'N';
  373. ftBCD, ftCurrency:
  374. if DbfVersion = xFoxPro then
  375. FNativeFieldType := 'Y';
  376. end;
  377. if FNativeFieldType = #0 then
  378. raise EDbfError.CreateFmt(STRING_INVALID_VCL_FIELD_TYPE, [GetDisplayName, Ord(FFieldType)]);
  379. end;
  380. procedure TDbfFieldDef.SetDefaultSize;
  381. begin
  382. // choose default values for variable size fields
  383. case FFieldType of
  384. ftFloat:
  385. begin
  386. FSize := 18;
  387. FPrecision := 9;
  388. end;
  389. ftCurrency, ftBCD:
  390. begin
  391. FSize := 8;
  392. FPrecision := 4;
  393. end;
  394. ftSmallInt, ftWord:
  395. begin
  396. FSize := DIGITS_SMALLINT;
  397. FPrecision := 0;
  398. end;
  399. ftInteger, ftAutoInc:
  400. begin
  401. if DbfVersion = xBaseVII then
  402. FSize := 4
  403. else
  404. FSize := DIGITS_INTEGER;
  405. FPrecision := 0;
  406. end;
  407. {$ifdef SUPPORT_INT64}
  408. ftLargeInt:
  409. begin
  410. FSize := DIGITS_LARGEINT;
  411. FPrecision := 0;
  412. end;
  413. {$endif}
  414. ftString {$ifdef SUPPORT_FIELDTYPES_V4}, ftFixedChar, ftWideString{$endif}:
  415. begin
  416. FSize := 30;
  417. FPrecision := 0;
  418. end;
  419. end; // case fieldtype
  420. // set sizes for fields that are restricted to single size/precision
  421. CheckSizePrecision;
  422. end;
  423. procedure TDbfFieldDef.CheckSizePrecision;
  424. begin
  425. case FNativeFieldType of
  426. 'C':
  427. begin
  428. if FSize < 0 then FSize := 0;
  429. if FSize >= 65534 then FSize := 65534;
  430. FPrecision := 0;
  431. end;
  432. 'L':
  433. begin
  434. FSize := 1;
  435. FPrecision := 0;
  436. end;
  437. 'N','F':
  438. begin
  439. // floating point
  440. if FSize < 1 then FSize := 1;
  441. if FSize >= 20 then FSize := 20;
  442. if FPrecision > FSize-2 then FPrecision := FSize-2;
  443. if FPrecision < 0 then FPrecision := 0;
  444. end;
  445. 'D':
  446. begin
  447. FSize := 8;
  448. FPrecision := 0;
  449. end;
  450. 'M','G','B':
  451. begin
  452. FSize := 10;
  453. FPrecision := 0;
  454. end;
  455. '+','I':
  456. begin
  457. FSize := 4;
  458. FPrecision := 0;
  459. end;
  460. '@', 'O':
  461. begin
  462. FSize := 8;
  463. FPrecision := 0;
  464. end;
  465. 'T':
  466. begin
  467. if DbfVersion = xFoxPro then
  468. FSize := 8
  469. else
  470. FSize := 14;
  471. FPrecision := 0;
  472. end;
  473. 'Y':
  474. begin
  475. FSize := 8;
  476. FPrecision := 4;
  477. end;
  478. else
  479. // Nothing
  480. end; // case
  481. end;
  482. function TDbfFieldDef.GetDisplayName: string; {override;}
  483. begin
  484. Result := FieldName;
  485. end;
  486. function TDbfFieldDef.IsBlob: Boolean; {override;}
  487. begin
  488. Result := FNativeFieldType in ['M','G','B'];
  489. end;
  490. procedure TDbfFieldDef.FreeBuffers;
  491. begin
  492. if FDefaultBuf <> nil then
  493. begin
  494. // one buffer for all
  495. FreeMemAndNil(Pointer(FDefaultBuf));
  496. FMinBuf := nil;
  497. FMaxBuf := nil;
  498. end;
  499. FAllocSize := 0;
  500. end;
  501. procedure TDbfFieldDef.AllocBuffers;
  502. begin
  503. // size changed?
  504. if FAllocSize <> FSize then
  505. begin
  506. // free old buffers
  507. FreeBuffers;
  508. // alloc new
  509. GetMem(FDefaultBuf, FSize*3);
  510. FMinBuf := FDefaultBuf + FSize;
  511. FMaxBuf := FMinBuf + FSize;
  512. // store allocated size
  513. FAllocSize := FSize;
  514. end;
  515. end;
  516. end.