bufdataset_parser.pp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. unit bufdataset_parser;
  2. {
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1999-2022 by Joost van der Sluis and other members of the
  5. Free Pascal development team
  6. BufDataset parser
  7. See the file COPYING.FPC, included in this distribution,
  8. for details about the copyright.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. **********************************************************************}
  13. {$h+}
  14. {$mode delphi}
  15. interface
  16. uses
  17. SysUtils,
  18. Classes,
  19. db,
  20. dbf_prscore,
  21. dbf_prsdef;
  22. type
  23. TBufDatasetParser = class(TCustomExpressionParser)
  24. private
  25. FDataset: TDataSet;
  26. FFieldVarList: TStringList;
  27. FResultLen: Integer;
  28. FIsExpression: Boolean; // expression or simple field?
  29. FFieldType: TExpressionType;
  30. FCaseInsensitive: Boolean;
  31. FPartialMatch: boolean;
  32. protected
  33. FCurrentExpression: string;
  34. procedure FillExpressList; override;
  35. procedure HandleUnknownVariable(VarName: string); override;
  36. function GetVariableInfo(const VarName: string): TField;
  37. function CurrentExpression: string; override;
  38. function GetResultType: TExpressionType; override;
  39. procedure SetCaseInsensitive(NewInsensitive: Boolean);
  40. procedure SetPartialMatch(NewPartialMatch: boolean);
  41. public
  42. constructor Create(ADataset: TDataset);
  43. destructor Destroy; override;
  44. procedure ClearExpressions; override;
  45. procedure ParseExpression(const AExpression: string); virtual;
  46. function ExtractFromBuffer(Buffer: TRecordBuffer): PChar; virtual;
  47. property Dataset: TDataSet read FDataset; // write FDataset;
  48. property Expression: string read FCurrentExpression;
  49. property ResultLen: Integer read FResultLen;
  50. property CaseInsensitive: Boolean read FCaseInsensitive write SetCaseInsensitive;
  51. property PartialMatch: boolean read FPartialMatch write SetPartialMatch;
  52. end;
  53. implementation
  54. uses dbconst;
  55. type
  56. // TFieldVar aids in retrieving field values from records
  57. // in their proper type
  58. TFieldVar = class(TObject)
  59. private
  60. FField: TField;
  61. FFieldName: string;
  62. FFieldIsNull: boolean;
  63. FExprWord: TExprWord;
  64. protected
  65. function GetFieldVal: Pointer; virtual; abstract;
  66. function GetFieldType: TExpressionType; virtual; abstract;
  67. function GetFieldIsNull: PBoolean;
  68. public
  69. constructor Create(UseField: TField);
  70. procedure Refresh(Buffer: TRecordBuffer); virtual; abstract;
  71. property FieldVal: Pointer read GetFieldVal;
  72. property FieldDef: TField read FField;
  73. property FieldType: TExpressionType read GetFieldType;
  74. property FieldName: string read FFieldName;
  75. property FieldIsNull: PBoolean read GetFieldIsNull;
  76. end;
  77. TStringFieldVar = class(TFieldVar)
  78. protected
  79. FFieldVal: PChar;
  80. function GetFieldVal: Pointer; override;
  81. function GetFieldType: TExpressionType; override;
  82. public
  83. constructor Create(UseField: TField);
  84. destructor Destroy; override;
  85. procedure Refresh(Buffer: TRecordBuffer); override;
  86. end;
  87. TFloatFieldVar = class(TFieldVar)
  88. private
  89. FFieldVal: Double;
  90. protected
  91. function GetFieldVal: Pointer; override;
  92. function GetFieldType: TExpressionType; override;
  93. public
  94. procedure Refresh(Buffer: TRecordBuffer); override;
  95. end;
  96. TIntegerFieldVar = class(TFieldVar)
  97. private
  98. FFieldVal: Integer;
  99. protected
  100. function GetFieldVal: Pointer; override;
  101. function GetFieldType: TExpressionType; override;
  102. public
  103. procedure Refresh(Buffer: TRecordBuffer); override;
  104. end;
  105. TLargeIntFieldVar = class(TFieldVar)
  106. private
  107. FFieldVal: Int64;
  108. protected
  109. function GetFieldVal: Pointer; override;
  110. function GetFieldType: TExpressionType; override;
  111. public
  112. procedure Refresh(Buffer: TRecordBuffer); override;
  113. end;
  114. TDateTimeFieldVar = class(TFieldVar)
  115. private
  116. FFieldVal: TDateTime;
  117. function GetFieldType: TExpressionType; override;
  118. protected
  119. function GetFieldVal: Pointer; override;
  120. public
  121. procedure Refresh(Buffer: TRecordBuffer); override;
  122. end;
  123. TBooleanFieldVar = class(TFieldVar)
  124. private
  125. FFieldVal: wordbool;
  126. function GetFieldType: TExpressionType; override;
  127. protected
  128. function GetFieldVal: Pointer; override;
  129. public
  130. procedure Refresh(Buffer: TRecordBuffer); override;
  131. end;
  132. TBCDFieldVar = class(TFloatFieldVar)
  133. public
  134. procedure Refresh(Buffer: TRecordBuffer); override;
  135. end;
  136. //--TFieldVar----------------------------------------------------------------
  137. constructor TFieldVar.Create(UseField: TField);
  138. begin
  139. inherited Create;
  140. // store field
  141. //FDataset := ADataset;
  142. FField := UseField;
  143. FFieldName := UseField.FieldName;
  144. end;
  145. function TFieldVar.GetFieldIsNull: PBoolean;
  146. begin
  147. Result := @FFieldIsNull;
  148. end;
  149. //--TStringFieldVar-------------------------------------------------------------
  150. function TStringFieldVar.GetFieldVal: Pointer;
  151. begin
  152. Result := @FFieldVal;
  153. end;
  154. function TStringFieldVar.GetFieldType: TExpressionType;
  155. begin
  156. Result := etString;
  157. end;
  158. constructor TStringFieldVar.Create(UseField: TField);
  159. begin
  160. inherited;
  161. GetMem(FFieldVal, dsMaxStringSize+1);
  162. end;
  163. destructor TStringFieldVar.Destroy;
  164. begin
  165. FreeMem(FFieldVal);
  166. inherited;
  167. end;
  168. procedure TStringFieldVar.Refresh(Buffer: TRecordBuffer);
  169. var Fieldbuf : TStringFieldBuffer;
  170. begin
  171. FFieldIsNull := not FField.DataSet.GetFieldData(FField,@Fieldbuf);
  172. if FFieldIsNull then
  173. FFieldVal^:=#0
  174. else
  175. strcopy(FFieldVal,@Fieldbuf[0]);
  176. end;
  177. //--TFloatFieldVar-----------------------------------------------------------
  178. function TFloatFieldVar.GetFieldVal: Pointer;
  179. begin
  180. Result := @FFieldVal;
  181. end;
  182. function TFloatFieldVar.GetFieldType: TExpressionType;
  183. begin
  184. Result := etFloat;
  185. end;
  186. procedure TFloatFieldVar.Refresh(Buffer: TRecordBuffer);
  187. begin
  188. FFieldIsNull := not FField.DataSet.GetFieldData(FField,@FFieldVal);
  189. if FFieldIsNull then
  190. FFieldVal := 0;
  191. end;
  192. //--TIntegerFieldVar----------------------------------------------------------
  193. function TIntegerFieldVar.GetFieldVal: Pointer;
  194. begin
  195. Result := @FFieldVal;
  196. end;
  197. function TIntegerFieldVar.GetFieldType: TExpressionType;
  198. begin
  199. Result := etInteger;
  200. end;
  201. procedure TIntegerFieldVar.Refresh(Buffer: TRecordBuffer);
  202. begin
  203. FFieldIsNull := not FField.DataSet.GetFieldData(FField,@FFieldVal);
  204. if FFieldIsNull then
  205. FFieldVal := 0;
  206. end;
  207. //--TLargeIntFieldVar----------------------------------------------------------
  208. function TLargeIntFieldVar.GetFieldVal: Pointer;
  209. begin
  210. Result := @FFieldVal;
  211. end;
  212. function TLargeIntFieldVar.GetFieldType: TExpressionType;
  213. begin
  214. Result := etLargeInt;
  215. end;
  216. procedure TLargeIntFieldVar.Refresh(Buffer: TRecordBuffer);
  217. begin
  218. FFieldIsNull := not FField.DataSet.GetFieldData(FField,@FFieldVal);
  219. if FFieldIsNull then
  220. FFieldVal := 0;
  221. end;
  222. //--TDateTimeFieldVar---------------------------------------------------------
  223. function TDateTimeFieldVar.GetFieldVal: Pointer;
  224. begin
  225. Result := @FFieldVal;
  226. end;
  227. function TDateTimeFieldVar.GetFieldType: TExpressionType;
  228. begin
  229. Result := etDateTime;
  230. end;
  231. procedure TDateTimeFieldVar.Refresh(Buffer:TRecordBuffer );
  232. begin
  233. FFieldIsNull := not FField.DataSet.GetFieldData(FField,@FFieldVal);
  234. if FFieldIsNull then
  235. FFieldVal := 0;
  236. end;
  237. //--TBooleanFieldVar---------------------------------------------------------
  238. function TBooleanFieldVar.GetFieldVal: Pointer;
  239. begin
  240. Result := @FFieldVal;
  241. end;
  242. function TBooleanFieldVar.GetFieldType: TExpressionType;
  243. begin
  244. Result := etBoolean;
  245. end;
  246. procedure TBooleanFieldVar.Refresh(Buffer: TRecordBuffer);
  247. begin
  248. FFieldIsNull := not FField.DataSet.GetFieldData(FField,@FFieldVal);
  249. if FFieldIsNull then
  250. FFieldVal := False;
  251. end;
  252. procedure TBCDFieldVar.Refresh(Buffer: TRecordBuffer);
  253. var c: currency;
  254. begin
  255. FFieldIsNull := not FField.DataSet.GetFieldData(FField,@c);
  256. if FFieldIsNull then
  257. FFieldVal := 0
  258. else
  259. FFieldVal := c;
  260. end;
  261. //--TBufDatasetParser---------------------------------------------------------------
  262. constructor TBufDatasetParser.Create(Adataset: TDataSet);
  263. begin
  264. FDataset := Adataset;
  265. FFieldVarList := TStringList.Create;
  266. FCaseInsensitive := true;
  267. inherited Create;
  268. end;
  269. destructor TBufDatasetParser.Destroy;
  270. begin
  271. ClearExpressions;
  272. inherited;
  273. FreeAndNil(FFieldVarList);
  274. end;
  275. function TBufDatasetParser.GetResultType: TExpressionType;
  276. begin
  277. // if not a real expression, return type ourself
  278. if FIsExpression then
  279. Result := inherited GetResultType
  280. else
  281. Result := FFieldType;
  282. end;
  283. procedure TBufDatasetParser.SetCaseInsensitive(NewInsensitive: Boolean);
  284. begin
  285. if FCaseInsensitive <> NewInsensitive then
  286. begin
  287. // clear and regenerate functions
  288. FCaseInsensitive := NewInsensitive;
  289. FillExpressList;
  290. end;
  291. end;
  292. procedure TBufDatasetParser.SetPartialMatch(NewPartialMatch: boolean);
  293. begin
  294. if FPartialMatch <> NewPartialMatch then
  295. begin
  296. // refill function list
  297. FPartialMatch := NewPartialMatch;
  298. FillExpressList;
  299. end;
  300. end;
  301. procedure TBufDatasetParser.FillExpressList;
  302. var
  303. lExpression: string;
  304. begin
  305. lExpression := FCurrentExpression;
  306. ClearExpressions;
  307. FWordsList.FreeAll;
  308. FWordsList.AddList(DbfWordsGeneralList, 0, DbfWordsGeneralList.Count - 1);
  309. if FCaseInsensitive then
  310. begin
  311. FWordsList.AddList(DbfWordsInsensGeneralList, 0, DbfWordsInsensGeneralList.Count - 1);
  312. if FPartialMatch then
  313. begin
  314. FWordsList.AddList(DbfWordsInsensPartialList, 0, DbfWordsInsensPartialList.Count - 1);
  315. end else begin
  316. FWordsList.AddList(DbfWordsInsensNoPartialList, 0, DbfWordsInsensNoPartialList.Count - 1);
  317. end;
  318. end else begin
  319. FWordsList.AddList(DbfWordsSensGeneralList, 0, DbfWordsSensGeneralList.Count - 1);
  320. if FPartialMatch then
  321. begin
  322. FWordsList.AddList(DbfWordsSensPartialList, 0, DbfWordsSensPartialList.Count - 1);
  323. end else begin
  324. FWordsList.AddList(DbfWordsSensNoPartialList, 0, DbfWordsSensNoPartialList.Count - 1);
  325. end;
  326. end;
  327. if Length(lExpression) > 0 then
  328. ParseExpression(lExpression);
  329. end;
  330. function TBufDatasetParser.GetVariableInfo(const VarName: string): TField;
  331. begin
  332. Result := FDataset.FindField(VarName);
  333. end;
  334. function TBufDatasetParser.CurrentExpression: string;
  335. begin
  336. Result := FCurrentExpression;
  337. end;
  338. procedure TBufDatasetParser.HandleUnknownVariable(VarName: string);
  339. var
  340. FieldInfo: TField;
  341. TempFieldVar: TFieldVar;
  342. begin
  343. // is this variable a fieldname?
  344. FieldInfo := GetVariableInfo(VarName);
  345. if FieldInfo = nil then
  346. raise EDatabaseError.CreateFmt(SErrIndexBasedOnUnkField, [VarName]);
  347. // define field in parser
  348. case FieldInfo.DataType of
  349. ftString, ftFixedChar:
  350. begin
  351. TempFieldVar := TStringFieldVar.Create(FieldInfo);
  352. TempFieldVar.FExprWord := DefineStringVariable(VarName, TempFieldVar.FieldVal, TempFieldVar.FieldIsNull);
  353. TempFieldVar.FExprWord.fixedlen := Fieldinfo.Size;
  354. end;
  355. ftBoolean:
  356. begin
  357. TempFieldVar := TBooleanFieldVar.Create(FieldInfo);
  358. TempFieldVar.FExprWord := DefineBooleanVariable(VarName, TempFieldVar.FieldVal);
  359. end;
  360. ftFloat:
  361. begin
  362. TempFieldVar := TFloatFieldVar.Create(FieldInfo);
  363. TempFieldVar.FExprWord := DefineFloatVariable(VarName, TempFieldVar.FieldVal);
  364. end;
  365. ftAutoInc, ftInteger, ftSmallInt, ftWord:
  366. begin
  367. TempFieldVar := TIntegerFieldVar.Create(FieldInfo);
  368. TempFieldVar.FExprWord := DefineIntegerVariable(VarName, TempFieldVar.FieldVal, TempFieldVar.FieldIsNull);
  369. end;
  370. ftLargeInt:
  371. begin
  372. TempFieldVar := TLargeIntFieldVar.Create(FieldInfo);
  373. TempFieldVar.FExprWord := DefineLargeIntVariable(VarName, TempFieldVar.FieldVal);
  374. end;
  375. ftDate, ftDateTime:
  376. begin
  377. TempFieldVar := TDateTimeFieldVar.Create(FieldInfo);
  378. TempFieldVar.FExprWord := DefineDateTimeVariable(VarName, TempFieldVar.FieldVal);
  379. end;
  380. ftBCD:
  381. begin
  382. TempFieldVar := TBCDFieldVar.Create(FieldInfo);
  383. TempFieldVar.FExprWord := DefineFloatVariable(VarName, TempFieldVar.FieldVal);
  384. end;
  385. else
  386. raise EDatabaseError.CreateFmt(SErrIndexBasedOnInvField, [VarName,Fieldtypenames[FieldInfo.DataType]]);
  387. end;
  388. // add to our own list
  389. FFieldVarList.AddObject(VarName, TempFieldVar);
  390. end;
  391. procedure TBufDatasetParser.ClearExpressions;
  392. var
  393. I: Integer;
  394. begin
  395. inherited;
  396. // test if already freed
  397. if FFieldVarList <> nil then
  398. begin
  399. // free field list
  400. for I := 0 to FFieldVarList.Count - 1 do
  401. begin
  402. // replacing with nil = undefining variable
  403. FWordsList.DoFree(TFieldVar(FFieldVarList.Objects[I]).FExprWord);
  404. TFieldVar(FFieldVarList.Objects[I]).Free;
  405. end;
  406. FFieldVarList.Clear;
  407. end;
  408. // clear expression
  409. FCurrentExpression := EmptyStr;
  410. end;
  411. procedure TBufDatasetParser.ParseExpression(const AExpression: string);
  412. var
  413. TempBuffer: TRecordBuffer;
  414. begin
  415. // clear any current expression
  416. ClearExpressions;
  417. // is this a simple field or complex expression?
  418. FIsExpression := GetVariableInfo(AExpression) = nil;
  419. if FIsExpression then
  420. begin
  421. // parse requested
  422. CompileExpression(AExpression);
  423. // determine length of string length expressions
  424. if ResultType = etString then
  425. begin
  426. // make empty record
  427. GetMem(TempBuffer, FDataset.RecordSize);
  428. try
  429. FillChar(TempBuffer^, FDataset.RecordSize, #0);
  430. FResultLen := StrLen(ExtractFromBuffer(TempBuffer));
  431. finally
  432. FreeMem(TempBuffer);
  433. end;
  434. end;
  435. end else begin
  436. // simple field, create field variable for it
  437. HandleUnknownVariable(AExpression);
  438. FFieldType := TFieldVar(FFieldVarList.Objects[0]).FieldType;
  439. // set result len of variable length fields
  440. if FFieldType = etString then
  441. FResultLen := TFieldVar(FFieldVarList.Objects[0]).FieldDef.Size
  442. end;
  443. // set result len for fixed length expressions / fields
  444. case ResultType of
  445. etBoolean: FResultLen := 1;
  446. etInteger: FResultLen := 4;
  447. etFloat: FResultLen := 8;
  448. etDateTime: FResultLen := 8;
  449. end;
  450. // check if expression not too long
  451. if FResultLen > 100 then
  452. raise EDatabaseError.CreateFmt(SErrIndexResultTooLong, [AExpression, FResultLen]);
  453. // if no errors, assign current expression
  454. FCurrentExpression := AExpression;
  455. end;
  456. function TBufDatasetParser.ExtractFromBuffer(Buffer: TRecordBuffer): PChar;
  457. var
  458. I: Integer;
  459. begin
  460. // prepare all field variables
  461. for I := 0 to FFieldVarList.Count - 1 do
  462. TFieldVar(FFieldVarList.Objects[I]).Refresh(Buffer);
  463. // complex expression?
  464. if FIsExpression then
  465. begin
  466. // execute expression
  467. EvaluateCurrent;
  468. Result := ExpResult;
  469. end else begin
  470. // simple field, get field result
  471. Result := TFieldVar(FFieldVarList.Objects[0]).FieldVal;
  472. // if string then dereference
  473. if FFieldType = etString then
  474. Result := PPChar(Result)^;
  475. end;
  476. end;
  477. end.