dbf_fields.pas 15 KB

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