2
0

sqldbwebdata.pp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2019 by the Free Pascal development team
  4. sqldb webdata interface
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit SQLDBWebData;
  12. {$mode objfpc}{$H+}
  13. interface
  14. uses
  15. Classes, SysUtils, fphttp, fpwebdata, DB, SQLDB;
  16. Type
  17. { TCustomSQLDBWebDataProvider }
  18. TNewIDEvent = Procedure(Sender : TObject; Out AID : String) of object;
  19. TGetParamTypeEvent = Procedure (Sender : TObject; Const ParamName,AValue : String; Var AType : TFieldtype) of object;
  20. TGetParamValueEvent = Procedure (Sender : TObject; P : TParam; Var Handled : Boolean) of object;
  21. TCustomSQLDBWebDataProvider = Class(TFPCustomWebDataProvider)
  22. private
  23. FIDFieldName: String;
  24. FONGetDataset: TNotifyEvent;
  25. FOnGetNewID: TNewIDEvent;
  26. FOnGetParamValue: TGetParamValueEvent;
  27. FParams: TParams;
  28. FSQLS : Array[0..3] of TStringList;
  29. FConnection: TSQLConnection;
  30. FQuery : TSQLQuery;
  31. FLastNewID : String;
  32. FOnGetParamType : TGetParamTypeEvent;
  33. function GetS(AIndex: integer): TStrings;
  34. procedure RegenerateParams;
  35. procedure SetConnection(const AValue: TSQLConnection);
  36. procedure SetParams(const AValue: TParams);
  37. procedure SetS(AIndex: integer; const AValue: TStrings);
  38. Protected
  39. function CheckDataset : Boolean; virtual;
  40. function CreateQuery(AOwner: TComponent; ATransaction: TSQLTransaction; ASQL: Tstrings): TSQLQuery;
  41. function GetParamType(P: TParam; const AValue: String): TFieldType; virtual;
  42. procedure SetTypedParam(P: TParam; Const AValue: String); virtual;
  43. procedure ExecuteSQL(ASQL: TStrings; Msg: String=''; DoNewID : Boolean = False); virtual;
  44. procedure ApplySQLParams(AQuery: TSQLQuery; DoNewID : Boolean = False); virtual;
  45. Procedure SQLChanged(Sender : TObject); virtual;
  46. Procedure DoUpdate; override;
  47. Procedure DoDelete; override;
  48. Procedure DoInsert; override;
  49. Procedure DoApplyParams; override;
  50. Function SQLQuery : TSQLQuery;
  51. Function GetDataset : TDataset; override;
  52. Function DoGetNewID : String; virtual;
  53. Function GetNewID : String;
  54. Function IDFieldValue : String; override;
  55. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  56. Property SelectSQL : TStrings Index 0 Read GetS Write SetS;
  57. Property UpdateSQL : TStrings Index 1 Read GetS Write SetS;
  58. Property DeleteSQL : TStrings Index 2 Read GetS Write SetS;
  59. Property InsertSQL : TStrings Index 3 Read GetS Write SetS;
  60. Property Connection : TSQLConnection Read FConnection Write SetConnection;
  61. Property OnGetNewID : TNewIDEvent Read FOnGetNewID Write FOnGetNewID;
  62. property OnGetParameterType : TGetParamTypeEvent Read FOnGetParamType Write FOnGetParamType;
  63. property OnGetParameterValue : TGetParamValueEvent Read FOnGetParamValue Write FOnGetParamValue;
  64. Property OnGetDataset : TNotifyEvent Read FONGetDataset Write FOnGetDataset;
  65. Property Params : TParams Read FParams Write SetParams;
  66. Public
  67. Constructor Create(AOwner : TComponent); override;
  68. Destructor Destroy; override;
  69. end;
  70. TSQLDBWebDataProvider = Class(TCustomSQLDBWebDataProvider)
  71. Published
  72. Property SelectSQL;
  73. Property UpdateSQL;
  74. Property DeleteSQL;
  75. Property InsertSQL;
  76. Property Connection;
  77. Property IDFieldName;
  78. Property OnGetNewID;
  79. property OnGetParameterType;
  80. property OnGetParameterValue;
  81. Property OnGetDataset;
  82. Property Options;
  83. Property Params;
  84. end;
  85. implementation
  86. { $define wmdebug}
  87. {$ifdef wmdebug}
  88. uses dbugintf;
  89. {$endif}
  90. resourcestring
  91. SErrNoSelectSQL = '%s: No select SQL statement provided.';
  92. SErrNoUpdateSQL = '%s: No update SQL statement provided.';
  93. SErrNoInsertSQL = '%s: No insert SQL statement provided.';
  94. SErrNoDeleteSQL = '%s: No delete SQL statement provided.';
  95. SErrUpdating = '%s: An error occurred during the update operation: %s';
  96. SErrDeleting = '%s: An error occurred during the delete operation: %s';
  97. SErrInserting = '%s: An error occurred during the insert operation: %s';
  98. SErrNoNewIDEvent = '%s : Cannot generate ID: No OnGetNewID event assigned.';
  99. { TCustomSQLDBWebDataProvider }
  100. function TCustomSQLDBWebDataProvider.GetS(AIndex: integer): TStrings;
  101. begin
  102. Result:=FSQLS[AIndex];
  103. end;
  104. procedure TCustomSQLDBWebDataProvider.SetConnection(const AValue: TSQLConnection
  105. );
  106. begin
  107. if (FConnection=AValue) then exit;
  108. If Assigned(FConnection) then
  109. FConnection.RemoveFreeNotification(Self);
  110. FConnection:=AValue;
  111. If Assigned(FConnection) then
  112. FConnection.FreeNotification(Self);
  113. end;
  114. procedure TCustomSQLDBWebDataProvider.SetParams(const AValue: TParams);
  115. begin
  116. if FParams=AValue then exit;
  117. FParams.Assign(AValue);
  118. end;
  119. procedure TCustomSQLDBWebDataProvider.SetS(AIndex: integer;
  120. const AValue: TStrings);
  121. begin
  122. FSQLS[AIndex].Assign(AValue);
  123. end;
  124. procedure TCustomSQLDBWebDataProvider.SQLChanged(Sender: TObject);
  125. begin
  126. If (Sender=SelectSQL) then
  127. begin
  128. if Assigned(FQuery) then
  129. begin
  130. FQuery.Close;
  131. FQuery.SQL.Assign(SelectSQL);
  132. end;
  133. If Not (csLoading in ComponentState) then
  134. RegenerateParams;
  135. end;
  136. end;
  137. procedure TCustomSQLDBWebDataProvider.RegenerateParams;
  138. Var
  139. S : String;
  140. begin
  141. S:=SelectSQL.Text;
  142. Params.Clear;
  143. Params.ParseSQL(S,True);
  144. end;
  145. procedure TCustomSQLDBWebDataProvider.ExecuteSQL(ASQL : TStrings; Msg : String = ''; DoNewID : Boolean = False);
  146. Var
  147. Q : TSQLQuery;
  148. begin
  149. {$ifdef wmdebug}SendDebug('Entering TCustomSQLDBWebDataProvider.ExecuteSQL');{$endif}
  150. Q:=CreateQuery(Nil,Nil,ASQL);
  151. try
  152. Q.Transaction.Active:=True;
  153. try
  154. ApplySQLParams(Q,DoNewID);
  155. Q.ExecSQL;
  156. (Q.Transaction as TSQLTransaction).Commit;
  157. except
  158. On E : Exception do
  159. begin
  160. (Q.Transaction as TSQLTransaction).Rollback;
  161. If (Msg<>'') then
  162. E.Message:=Format(Msg,[Self.Name,E.Message]);
  163. Raise;
  164. end;
  165. end
  166. finally
  167. Q.Free;
  168. end;
  169. {$ifdef wmdebug}SendDebug('Exiting TCustomSQLDBWebDataProvider.ExecuteSQL');{$endif}
  170. end;
  171. procedure TCustomSQLDBWebDataProvider.DoUpdate;
  172. begin
  173. {$ifdef wmdebug}SendDebug('Entering TCustomSQLDBWebDataProvider.DoUpdate');{$endif}
  174. If (Trim(UpdateSQL.Text)='') then
  175. Raise EFPHTTPError.CreateFmt(SErrNoUpdateSQL,[Self.Name]);
  176. FLastNewID:='';
  177. ExecuteSQL(UpdateSQL,SErrUpdating);
  178. {$ifdef wmdebug}SendDebug('Exiting TCustomSQLDBWebDataProvider.DoUpdate');{$endif}
  179. end;
  180. procedure TCustomSQLDBWebDataProvider.DoDelete;
  181. begin
  182. {$ifdef wmdebug}SendDebug('Entering TCustomSQLDBWebDataProvider.DoDelete');{$endif}
  183. If (Trim(DeleteSQL.Text)='') then
  184. Raise EFPHTTPError.CreateFmt(SErrNoDeleteSQL,[Self.Name]);
  185. FLastNewID:='';
  186. ExecuteSQL(DeleteSQL,SErrDeleting);
  187. {$ifdef wmdebug}SendDebug('Exiting TCustomSQLDBWebDataProvider.DoDelete');{$endif}
  188. end;
  189. procedure TCustomSQLDBWebDataProvider.DoInsert;
  190. begin
  191. {$ifdef wmdebug}SendDebug('Entering TCustomSQLDBWebDataProvider.DoInsert');{$endif}
  192. If (Trim(InsertSQL.Text)='') then
  193. Raise EFPHTTPError.CreateFmt(SErrNoInsertSQL,[Self.Name]);
  194. FLastNewID:='';
  195. ExecuteSQL(InsertSQL,SErrInserting,(IDFieldName<>''));
  196. {$ifdef wmdebug}SendDebug('Exiting TCustomSQLDBWebDataProvider.DoInsert');{$endif}
  197. end;
  198. procedure TCustomSQLDBWebDataProvider.Notification(AComponent: TComponent;
  199. Operation: TOperation);
  200. begin
  201. inherited;
  202. If (Operation=opRemove) then
  203. begin
  204. If (AComponent=FQuery) then
  205. FQuery:=Nil
  206. else if (AComponent=FConnection) then
  207. FConnection:=Nil;
  208. end;
  209. end;
  210. Function TCustomSQLDBWebDataProvider.CreateQuery(AOwner : TComponent; ATransaction : TSQLTransaction; ASQL : Tstrings) : TSQLQuery;
  211. begin
  212. Result:=TSQLQuery.Create(AOwner);
  213. If (AOwner<>Self) then
  214. Result.FreeNotification(Self);
  215. Result.DataBase:=Connection;
  216. If ATransaction=Nil then
  217. begin
  218. ATransaction:=TSQLTransaction.Create(Result);
  219. ATransaction.DataBase:=Connection;
  220. end;
  221. Result.Transaction:=ATransaction;
  222. Result.SQL.Assign(ASQL);
  223. end;
  224. Function TCustomSQLDBWebDataProvider.CheckDataset : boolean;
  225. begin
  226. {$ifdef wmdebug}SendDebug('Entering CheckDataset');{$endif}
  227. If (Trim(SelectSQL.Text)='') then
  228. Raise EFPHTTPError.CreateFmt(SErrNoSelectSQL,[Self.Name]);
  229. Result:=FQuery=Nil;
  230. If (Result) then
  231. FQuery:=CreateQuery(Nil,Nil,SelectSQL)
  232. else if not FQuery.Active then
  233. FQuery.SQL.Assign(SelectSQL);
  234. {$ifdef wmdebug}SendDebug('Exiting CheckDataset');{$endif}
  235. end;
  236. Function TCustomSQLDBWebDataProvider.GetParamType(P : TParam; Const AValue : String) : TFieldType;
  237. begin
  238. Result:=ftunknown;
  239. If Assigned(FOnGetParamType) then
  240. FOnGetParamType(Self,P.Name,AValue,Result);
  241. end;
  242. procedure TCustomSQLDBWebDataProvider.SetTypedParam(P : TParam; Const AValue : String);
  243. Var
  244. I : Integer;
  245. Q : Int64;
  246. D : TDateTime;
  247. ft : TFieldType;
  248. F : Double;
  249. B : Boolean;
  250. C : Currency;
  251. begin
  252. ft:=GetParamtype(P,AValue);
  253. If (AValue='') and (not (ft in [ftString,ftFixedChar,ftWideString,ftFixedWideChar])) then
  254. begin
  255. P.Clear;
  256. exit;
  257. end;
  258. If (ft<>ftUnknown) then
  259. begin
  260. try
  261. case ft of
  262. ftInteger,
  263. ftword,
  264. ftsmallint : I:=StrToInt(AValue);
  265. ftDate : D:=StrToDate(AValue);
  266. ftDateTime,
  267. ftTimestamp : D:=StrToDateTime(AValue);
  268. ftBoolean : B:=StrToBool(AValue);
  269. ftTime : D:=StrToTime(AValue);
  270. ftLargeint : Q:=StrToInt64(AValue);
  271. ftCurrency : C:=StrToCurr(Avalue);
  272. else
  273. ft:=ftString
  274. end
  275. except
  276. ft:=ftUnknown
  277. end;
  278. end;
  279. If (ft=ftUnknown) and (Length(AValue)<30) then
  280. begin
  281. if TryStrToInt(Avalue,I) then
  282. ft:=ftInteger
  283. else if TryStrToInt64(Avalue,Q) then
  284. ft:=ftInteger
  285. else if (Pos(DateSeparator,AValue)<>0) then
  286. begin
  287. if (Pos(TimeSeparator,AValue)<>0) and TryStrToDateTime(Avalue,D) then
  288. ft:=ftDateTime
  289. else if TryStrToDate(Avalue,D) then
  290. ft:=ftDate
  291. end
  292. else If (Pos(TimeSeparator,AValue)<>0) and TryStrToTime(Avalue,D) then
  293. ft:=ftTime
  294. else if (Pos(DecimalSeparator,AValue)<>0) then
  295. begin
  296. if trystrtofloat(AValue,F) then
  297. ft:=ftFloat
  298. else if TryStrToCurr(Avalue,C) then
  299. ft:=ftCurrency
  300. end
  301. else if TryStrToBool(Avalue,B) then
  302. ft:=ftBoolean
  303. end;
  304. Case ft of
  305. ftInteger,
  306. ftword,
  307. ftsmallint : P.AsInteger:=I;
  308. ftBoolean : P.AsBoolean:=B;
  309. ftLargeInt : P.AsLargeInt:=Q;
  310. ftDate : P.AsDate:=D;
  311. ftDateTime,
  312. ftTimestamp : P.AsDateTime:=D;
  313. ftTime : P.AsTime:=D;
  314. ftFloat,
  315. ftBCD,
  316. ftFMTBCD : P.AsFloat:=F;
  317. ftCurrency : P.AsCurrency:=F;
  318. else
  319. P.AsString:=AValue;
  320. end;
  321. end;
  322. procedure TCustomSQLDBWebDataProvider.ApplySQLParams(AQuery : TSQLQuery; DoNewID : Boolean = False);
  323. function TryAdaptor (const aName: string; P: TParam) : boolean;
  324. var S : string;
  325. begin
  326. result := Adaptor.TryFieldValue(aName,S);
  327. if not result then
  328. result := Adaptor.TryParamValue(aName,S);
  329. if result then
  330. SetTypedParam(P,S);
  331. end;
  332. var
  333. I: Integer;
  334. P : TParam;
  335. S : String;
  336. B : Boolean;
  337. begin
  338. {$ifdef wmdebug}SendDebug('Entering ApplySQLPArams');{$endif}
  339. For I:=0 to AQuery.Params.Count-1 do
  340. begin
  341. P:=AQuery.Params[i];
  342. B:=Assigned(FOnGetParamValue);
  343. if B then
  344. FOnGetParamValue(Self,P,B);
  345. if not B then
  346. begin
  347. If (P.Name=IDFieldName) then
  348. begin
  349. if DoNewID then
  350. begin
  351. GetNewID;
  352. SetTypedParam(P,FLastNewID)
  353. end
  354. else
  355. begin
  356. if not TryAdaptor (P.Name, P) then
  357. TryAdaptor('ID', P);
  358. end;
  359. end
  360. else if not TryAdaptor (P.Name, P) then
  361. P.Clear;
  362. end;
  363. end;
  364. {$ifdef wmdebug}SendDebug('Exiting ApplySQLPArams');{$endif}
  365. end;
  366. procedure TCustomSQLDBWebDataProvider.DoApplyParams;
  367. begin
  368. CheckDataset;
  369. ApplySQLParams(FQuery);
  370. end;
  371. function TCustomSQLDBWebDataProvider.SQLQuery: TSQLQuery;
  372. begin
  373. Result:=FQuery;
  374. end;
  375. function TCustomSQLDBWebDataProvider.GetDataset: TDataset;
  376. begin
  377. {$ifdef wmdebug}SendDebug('Get dataset: checking dataset');{$endif}
  378. If Assigned(FonGetDataset) then
  379. FOnGetDataset(Self);
  380. CheckDataset;
  381. FLastNewID:='';
  382. Result:=FQuery;
  383. {$ifdef wmdebug}SendDebug('Get dataset: activating transaction');{$endif}
  384. If Not FQuery.Transaction.Active then
  385. FQuery.Transaction.Active:=True;
  386. {$ifdef wmdebug}SendDebug('Get dataset: done');{$endif}
  387. end;
  388. function TCustomSQLDBWebDataProvider.DoGetNewID: String;
  389. begin
  390. If Not Assigned(FOnGetNewID) then
  391. Raise EFPHTTPError.CreateFmt(SErrNoNewIDEvent,[Self.Name]);
  392. FOnGetNewID(Self,Result);
  393. end;
  394. function TCustomSQLDBWebDataProvider.GetNewID: String;
  395. begin
  396. Result:=DoGetNewID;
  397. FLastNewID:=Result;
  398. end;
  399. function TCustomSQLDBWebDataProvider.IDFieldValue: String;
  400. begin
  401. {$ifdef wmdebug}SendDebug('Entering IDFieldValue');{$endif}
  402. If (FLastNewID<>'') then
  403. Result:=FLastNewID
  404. else If (IDFieldName<>'') then
  405. begin
  406. If not Adaptor.TryParamValue(IDFieldName,Result) then
  407. If not Adaptor.TryFieldValue(IDFieldName,Result) then
  408. Result:=inherited IDFieldValue;
  409. end
  410. else
  411. Result:=inherited IDFieldValue;
  412. {$ifdef wmdebug}SendDebug('Exiting IDFieldValue : '+Result);{$endif}
  413. end;
  414. constructor TCustomSQLDBWebDataProvider.Create(AOwner: TComponent);
  415. Var
  416. I : Integer;
  417. L : TStringList;
  418. begin
  419. inherited Create(AOwner);
  420. For I:=0 to 3 do
  421. begin
  422. L:=TStringList.Create;
  423. L.OnChange:=@SQLChanged;
  424. FSQLS[i]:=L;
  425. end;
  426. FParams:=TParams.Create(TParam);
  427. end;
  428. destructor TCustomSQLDBWebDataProvider.Destroy;
  429. Var
  430. I: Integer;
  431. begin
  432. For I:=0 to 3 do
  433. FreeAndNil(FSQLS[i]);
  434. Connection:=Nil;
  435. FreeAndNil(FQuery);
  436. FreeAndNil(FParams);
  437. inherited Destroy;
  438. end;
  439. end.