dbf_fields.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  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(ACollection: 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. if Size <> 0 then
  157. FieldDef.Size := Size;
  158. FieldDef.Required := Required;
  159. end;
  160. //====================================================================
  161. // DbfFieldDef
  162. //====================================================================
  163. constructor TDbfFieldDef.Create(ACollection: TCollection); {virtual}
  164. begin
  165. inherited;
  166. FDefaultBuf := nil;
  167. FMinBuf := nil;
  168. FMaxBuf := nil;
  169. FAllocSize := 0;
  170. FCopyFrom := -1;
  171. FPrecision := 0;
  172. FHasDefault := false;
  173. FHasMin := false;
  174. FHasMax := false;
  175. FNullPosition := -1;
  176. end;
  177. destructor TDbfFieldDef.Destroy; {override}
  178. begin
  179. FreeBuffers;
  180. inherited;
  181. end;
  182. procedure TDbfFieldDef.Assign(Source: TPersistent);
  183. var
  184. DbfSource: TDbfFieldDef;
  185. begin
  186. if Source is TDbfFieldDef then
  187. begin
  188. // copy from another TDbfFieldDef
  189. DbfSource := TDbfFieldDef(Source);
  190. FFieldName := DbfSource.FieldName;
  191. FFieldType := DbfSource.FieldType;
  192. FNativeFieldType := DbfSource.NativeFieldType;
  193. FSize := DbfSource.Size;
  194. FPrecision := DbfSource.Precision;
  195. FRequired := DbfSource.Required;
  196. FCopyFrom := DbfSource.Index;
  197. FIsLockField := DbfSource.IsLockField;
  198. FNullPosition := DbfSource.NullPosition;
  199. // copy default,min,max
  200. AllocBuffers;
  201. if DbfSource.DefaultBuf <> nil then
  202. Move(DbfSource.DefaultBuf^, FDefaultBuf^, FAllocSize*3);
  203. FHasDefault := DbfSource.HasDefault;
  204. FHasMin := DbfSource.HasMin;
  205. FHasMax := DbfSource.HasMax;
  206. // do we need offsets?
  207. FOffset := DbfSource.Offset;
  208. FAutoInc := DbfSource.AutoInc;
  209. {$ifdef SUPPORT_FIELDDEF_TPERSISTENT}
  210. end else if Source is TFieldDef then begin
  211. AssignDb(TFieldDef(Source));
  212. {$endif}
  213. end else
  214. inherited Assign(Source);
  215. end;
  216. procedure TDbfFieldDef.AssignDb(DbSource: TFieldDef);
  217. begin
  218. // copy from Db.TFieldDef
  219. FFieldName := DbSource.Name;
  220. FFieldType := DbSource.DataType;
  221. FSize := DbSource.Size;
  222. FPrecision := DbSource.Precision;
  223. FRequired := DbSource.Required;
  224. {$ifdef SUPPORT_FIELDDEF_INDEX}
  225. FCopyFrom := DbSource.Index;
  226. {$endif}
  227. FIsLockField := false;
  228. // convert VCL fieldtypes to native DBF fieldtypes
  229. VCLToNative;
  230. // for integer / float fields try fill in size/precision
  231. CheckSizePrecision;
  232. // VCL does not have default value support
  233. AllocBuffers;
  234. FHasDefault := false;
  235. FHasMin := false;
  236. FHasMax := false;
  237. FOffset := 0;
  238. FAutoInc := 0;
  239. end;
  240. procedure TDbfFieldDef.AssignTo(Dest: TPersistent);
  241. var
  242. DbDest: TFieldDef;
  243. begin
  244. {$ifdef SUPPORT_FIELDDEF_TPERSISTENT}
  245. // copy to VCL fielddef?
  246. if Dest is TFieldDef then
  247. begin
  248. DbDest := TFieldDef(Dest);
  249. // VCL TFieldDef does not know how to handle TDbfFieldDef!
  250. // what a shame :-)
  251. {$ifdef SUPPORT_FIELDDEF_ATTRIBUTES}
  252. DbDest.Attributes := [];
  253. DbDest.ChildDefs.Clear;
  254. DbDest.DataType := FFieldType;
  255. DbDest.Required := FRequired;
  256. DbDest.Size := FSize;
  257. DbDest.Name := FFieldName;
  258. {$endif}
  259. end else
  260. {$endif}
  261. inherited AssignTo(Dest);
  262. end;
  263. function TDbfFieldDef.GetDbfVersion: TXBaseVersion;
  264. begin
  265. Result := TDbfFieldDefs(Collection).DbfVersion;
  266. end;
  267. procedure TDbfFieldDef.SetFieldType(lFieldType: tFieldType);
  268. begin
  269. FFieldType := lFieldType;
  270. VCLToNative;
  271. SetDefaultSize;
  272. end;
  273. procedure TDbfFieldDef.SetNativeFieldType(lFieldType: tDbfFieldType);
  274. begin
  275. // get uppercase field type
  276. if (lFieldType >= 'a') and (lFieldType <= 'z') then
  277. lFieldType := Chr(Ord(lFieldType)-32);
  278. FNativeFieldType := lFieldType;
  279. NativeToVCL;
  280. CheckSizePrecision;
  281. end;
  282. procedure TDbfFieldDef.SetSize(lSize: Integer);
  283. begin
  284. FSize := lSize;
  285. CheckSizePrecision;
  286. end;
  287. procedure TDbfFieldDef.SetPrecision(lPrecision: Integer);
  288. begin
  289. FPrecision := lPrecision;
  290. CheckSizePrecision;
  291. end;
  292. procedure TDbfFieldDef.NativeToVCL;
  293. begin
  294. case FNativeFieldType of
  295. // OH 2000-11-15 dBase7 support.
  296. // Add the new fieldtypes
  297. '+' : FFieldType := ftAutoInc;
  298. 'I' : FFieldType := ftInteger;
  299. 'O' : FFieldType := ftFloat;
  300. '@', 'T':
  301. FFieldType := ftDateTime;
  302. 'C',
  303. #$91 {Russian 'C'}
  304. : FFieldType := ftString;
  305. 'L' : FFieldType := ftBoolean;
  306. 'F', 'N':
  307. begin
  308. if (FPrecision = 0) then
  309. begin
  310. if FSize <= DIGITS_SMALLINT then
  311. FFieldType := ftSmallInt
  312. else
  313. if TDbfFieldDefs(Collection).UseFloatFields then
  314. FFieldType := ftFloat
  315. else
  316. {$ifdef SUPPORT_INT64}
  317. if FSize <= DIGITS_INTEGER then
  318. FFieldType := ftInteger
  319. else
  320. FFieldType := ftLargeInt;
  321. {$else}
  322. FFieldType := ftInteger;
  323. {$endif}
  324. end else begin
  325. FFieldType := ftFloat;
  326. end;
  327. end;
  328. 'D' : FFieldType := ftDate;
  329. 'M' : FFieldType := ftMemo;
  330. 'B' :
  331. if DbfVersion = xFoxPro then
  332. FFieldType := ftFloat
  333. else
  334. FFieldType := ftBlob;
  335. 'G' : FFieldType := ftDBaseOle;
  336. 'Y' :
  337. if DbfGlobals.CurrencyAsBCD then
  338. FFieldType := ftBCD
  339. else
  340. FFieldType := ftCurrency;
  341. '0' : FFieldType := ftBytes; { Visual FoxPro ``_NullFlags'' }
  342. else
  343. FNativeFieldType := #0;
  344. FFieldType := ftUnknown;
  345. end; //case
  346. end;
  347. procedure TDbfFieldDef.VCLToNative;
  348. begin
  349. FNativeFieldType := #0;
  350. case FFieldType of
  351. ftAutoInc : FNativeFieldType := '+';
  352. ftDateTime :
  353. if DbfVersion = xBaseVII then
  354. FNativeFieldType := '@'
  355. else
  356. if DbfVersion = xFoxPro then
  357. FNativeFieldType := 'T'
  358. else
  359. FNativeFieldType := 'D';
  360. {$ifdef SUPPORT_FIELDTYPES_V4}
  361. ftFixedChar,
  362. ftWideString,
  363. {$endif}
  364. ftString : FNativeFieldType := 'C';
  365. ftBoolean : FNativeFieldType := 'L';
  366. ftFloat, ftSmallInt, ftWord
  367. {$ifdef SUPPORT_INT64}
  368. , ftLargeInt
  369. {$endif}
  370. : FNativeFieldType := 'N';
  371. ftDate : FNativeFieldType := 'D';
  372. ftMemo : FNativeFieldType := 'M';
  373. ftBlob : FNativeFieldType := 'B';
  374. ftDBaseOle : FNativeFieldType := 'G';
  375. ftInteger :
  376. if DbfVersion = xBaseVII then
  377. FNativeFieldType := 'I'
  378. else
  379. FNativeFieldType := 'N';
  380. ftBCD, ftCurrency:
  381. if DbfVersion = xFoxPro then
  382. FNativeFieldType := 'Y';
  383. end;
  384. if FNativeFieldType = #0 then
  385. raise EDbfError.CreateFmt(STRING_INVALID_VCL_FIELD_TYPE, [GetDisplayName, Ord(FFieldType)]);
  386. end;
  387. procedure TDbfFieldDef.SetDefaultSize;
  388. begin
  389. // choose default values for variable size fields
  390. case FFieldType of
  391. ftFloat:
  392. begin
  393. FSize := 18;
  394. FPrecision := 9;
  395. end;
  396. ftCurrency, ftBCD:
  397. begin
  398. FSize := 8;
  399. FPrecision := 4;
  400. end;
  401. ftSmallInt, ftWord:
  402. begin
  403. FSize := DIGITS_SMALLINT;
  404. FPrecision := 0;
  405. end;
  406. ftInteger, ftAutoInc:
  407. begin
  408. if DbfVersion = xBaseVII then
  409. FSize := 4
  410. else
  411. FSize := DIGITS_INTEGER;
  412. FPrecision := 0;
  413. end;
  414. {$ifdef SUPPORT_INT64}
  415. ftLargeInt:
  416. begin
  417. FSize := DIGITS_LARGEINT;
  418. FPrecision := 0;
  419. end;
  420. {$endif}
  421. ftString {$ifdef SUPPORT_FIELDTYPES_V4}, ftFixedChar, ftWideString{$endif}:
  422. begin
  423. FSize := 30;
  424. FPrecision := 0;
  425. end;
  426. end; // case fieldtype
  427. // set sizes for fields that are restricted to single size/precision
  428. CheckSizePrecision;
  429. end;
  430. procedure TDbfFieldDef.CheckSizePrecision;
  431. begin
  432. case FNativeFieldType of
  433. 'C':
  434. begin
  435. if FSize < 0 then
  436. FSize := 0;
  437. if DbfVersion = xFoxPro then
  438. begin
  439. if FSize >= $FFFF then
  440. FSize := $FFFF;
  441. end else begin
  442. if FSize >= $FF then
  443. FSize := $FF;
  444. end;
  445. FPrecision := 0;
  446. end;
  447. 'L':
  448. begin
  449. FSize := 1;
  450. FPrecision := 0;
  451. end;
  452. 'N','F':
  453. begin
  454. // floating point
  455. if FSize < 1 then FSize := 1;
  456. if FSize >= 20 then FSize := 20;
  457. if FPrecision > FSize-2 then FPrecision := FSize-2;
  458. if FPrecision < 0 then FPrecision := 0;
  459. end;
  460. 'D':
  461. begin
  462. FSize := 8;
  463. FPrecision := 0;
  464. end;
  465. 'M','G','B':
  466. begin
  467. if DbfVersion = xFoxPro then
  468. FSize := 4
  469. else
  470. FSize := 10;
  471. FPrecision := 0;
  472. end;
  473. '+','I':
  474. begin
  475. FSize := 4;
  476. FPrecision := 0;
  477. end;
  478. '@', 'O':
  479. begin
  480. FSize := 8;
  481. FPrecision := 0;
  482. end;
  483. 'T':
  484. begin
  485. if DbfVersion = xFoxPro then
  486. FSize := 8
  487. else
  488. FSize := 14;
  489. FPrecision := 0;
  490. end;
  491. 'Y':
  492. begin
  493. FSize := 8;
  494. FPrecision := 4;
  495. end;
  496. else
  497. // Nothing
  498. end; // case
  499. end;
  500. function TDbfFieldDef.GetDisplayName: string; {override;}
  501. begin
  502. Result := FieldName;
  503. end;
  504. function TDbfFieldDef.IsBlob: Boolean; {override;}
  505. begin
  506. Result := FNativeFieldType in ['M','G','B'];
  507. end;
  508. procedure TDbfFieldDef.FreeBuffers;
  509. begin
  510. if FDefaultBuf <> nil then
  511. begin
  512. // one buffer for all
  513. FreeMemAndNil(Pointer(FDefaultBuf));
  514. FMinBuf := nil;
  515. FMaxBuf := nil;
  516. end;
  517. FAllocSize := 0;
  518. end;
  519. procedure TDbfFieldDef.AllocBuffers;
  520. begin
  521. // size changed?
  522. if FAllocSize <> FSize then
  523. begin
  524. // free old buffers
  525. FreeBuffers;
  526. // alloc new
  527. GetMem(FDefaultBuf, FSize*3);
  528. FMinBuf := FDefaultBuf + FSize;
  529. FMaxBuf := FMinBuf + FSize;
  530. // store allocated size
  531. FAllocSize := FSize;
  532. end;
  533. end;
  534. end.