fpreportdatasqldb.pp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. {
  2. This file is part of the Free Component Library.
  3. Copyright (c) 2017 Michael Van Canneyt, member of the Free Pascal development team
  4. Report Designer Data connector for SQLDB based data.
  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 fpreportdatasqldb;
  12. {$mode objfpc}{$H+}
  13. interface
  14. uses
  15. Classes, SysUtils, strutils, sqldb, db, fpjson, fpreportdata;
  16. Const
  17. keyConnection = 'connection';
  18. keySQL = 'sql';
  19. keyType = 'dbtype';
  20. keyHostName = 'host';
  21. keyDatabaseName = 'database';
  22. keyUserName = 'user';
  23. keyPassword = 'pwd';
  24. keyRole = 'role';
  25. keyParams = 'params';
  26. KeyCharSet = 'charset';
  27. keyHash = 'FPCRulez';
  28. Resourcestring
  29. SErrNoConnectionData = 'No connection data available';
  30. SErrNoSQL = 'No SQL statement set';
  31. Type
  32. { TFPReportConnector }
  33. TFPReportConnector = Class(TSQLConnector)
  34. Private
  35. FRefCount: Integer;
  36. Class procedure init;
  37. class procedure done;
  38. Class var
  39. FPool : TStringList;
  40. Public
  41. Procedure LoadFromConfig(aConfig : TJSONObject);
  42. class function CreateConnection(aConfig: TJSONObject): TFPReportConnector; virtual;
  43. Class Function TestConnection (aConfig : TJSONObject) : string; virtual;
  44. class function CreateDataset(aOwner: TComponent; aConfig: TJSONObject): TSQLQuery;
  45. class function CreateConfigHash(aConfig: TJSONObject): String;
  46. Class Procedure StartRender(ADataset : TDataset); virtual;
  47. Class Procedure EndRender(ADataset : TDataset); virtual;
  48. Class procedure CheckDBRelease;
  49. Property RefCount : Integer Read FRefCount;
  50. end;
  51. { TFPReportQuery }
  52. TFPReportQuery = class(TSQLQuery)
  53. Public
  54. Constructor Create(AOwner : TComponent); override;
  55. Destructor Destroy; override;
  56. end;
  57. { TReportSQLtransaction }
  58. TFPReportSQLtransaction = Class(TSQLTransaction)
  59. private
  60. FStartRefCount: Integer;
  61. Public
  62. Procedure StartRender;
  63. Procedure EndRender;
  64. Property StartRefCount : Integer Read FStartRefCount;
  65. end;
  66. { TSQLDBReportDataHandler }
  67. TSQLDBReportDataHandler = Class(TFPReportDataHandler)
  68. Function CreateDataset(AOwner : TComponent; AConfig : TJSONObject) : TDataset; override;
  69. Class Procedure StartRender(ADataset : TDataset); override;
  70. Class Procedure EndRender(ADataset : TDataset); override;
  71. Class Function CheckConfig(AConfig: TJSONObject): String; override;
  72. Class Function DataType : String; override;
  73. Class Function DataTypeDescription : String; override;
  74. Class Function AllowMasterDetail: Boolean; override;
  75. Class Procedure SetMasterDataset(ADetail, AMaster: TDataset); override;
  76. end;
  77. implementation
  78. { TFPReportSQLtransaction }
  79. procedure TFPReportSQLtransaction.StartRender;
  80. Var
  81. Start : Boolean;
  82. begin
  83. Start:=(FStartRefCount=0);
  84. Inc(FStartRefCount);
  85. if Start and not Active then
  86. StartTransaction;
  87. end;
  88. procedure TFPReportSQLtransaction.EndRender;
  89. begin
  90. if FStartRefCount>0 then
  91. begin
  92. Dec(FStartRefCount);
  93. If FStartRefCount=0 then
  94. RollBack;
  95. end;
  96. end;
  97. { TFPReportQuery }
  98. constructor TFPReportQuery.Create(AOwner: TComponent);
  99. begin
  100. inherited Create(AOwner);
  101. ReadOnly:=True;
  102. end;
  103. destructor TFPReportQuery.Destroy;
  104. begin
  105. If Database is TFPReportConnector then
  106. Dec(TFPReportConnector(Database).FRefCount);
  107. inherited Destroy;
  108. TFPReportConnector.CheckDBRelease;
  109. end;
  110. { TFPReportConnector }
  111. class procedure TFPReportConnector.init;
  112. begin
  113. FPool:=TStringList.Create;
  114. FPool.OwnsObjects:=True;
  115. FPool.Sorted:=True;
  116. FPool.Duplicates:=dupError;
  117. end;
  118. class procedure TFPReportConnector.done;
  119. begin
  120. FreeAndNil(FPool);
  121. end;
  122. Class Function TFPReportConnector.CreateConfigHash(aConfig : TJSONObject) : String;
  123. Procedure AH(N,V : String);
  124. begin
  125. if (V<>'') then
  126. Result:=Result+';'+N+'='+V;
  127. end;
  128. Procedure AH(N : String);
  129. begin
  130. AH(N,aConfig.get(N,''));
  131. end;
  132. Var
  133. A : TJSONArray;
  134. I : Integer;
  135. begin
  136. AH(keyType);
  137. AH(keyHostName);
  138. AH(keyDatabaseName);
  139. AH(keyUserName);
  140. AH(keyPassword);
  141. AH(keyRole);
  142. A:=aConfig.get(keyParams,TJSONArray(Nil));
  143. If Assigned(A) then
  144. For I:=0 to A.Count-1 do
  145. AH(IntToStr(I),A.Strings[i]);
  146. end;
  147. class procedure TFPReportConnector.StartRender(ADataset: TDataset);
  148. var
  149. Q : TFPReportQuery;
  150. T : TFPReportSQLTransaction;
  151. begin
  152. if (aDataset is TFPReportQuery) then
  153. begin
  154. Q:=aDataset as TFPReportQuery;
  155. if Q.Transaction is TFPReportSQLTransaction then
  156. begin
  157. T:=Q.Transaction as TFPReportSQLTransaction;
  158. T.StartRender;
  159. end;
  160. end;
  161. end;
  162. class procedure TFPReportConnector.EndRender(ADataset: TDataset);
  163. var
  164. Q : TFPReportQuery;
  165. T : TFPReportSQLTransaction;
  166. begin
  167. if (aDataset is TFPReportQuery) then
  168. begin
  169. Q:=aDataset as TFPReportQuery;
  170. if Q.Transaction is TFPReportSQLTransaction then
  171. begin
  172. T:=Q.Transaction as TFPReportSQLTransaction;
  173. T.EndRender;
  174. end;
  175. end;
  176. end;
  177. class procedure TFPReportConnector.CheckDBRelease;
  178. Var
  179. I : Integer;
  180. begin
  181. For I:=FPool.Count-1 downto 0 do
  182. begin
  183. // Writeln('Connection count for ',FPool[i], ' : ',TFPReportConnector(FPool.Objects[i]).FRefCount);
  184. if TFPReportConnector(FPool.Objects[i]).FRefCount=0 then
  185. FPool.Delete(I);
  186. end;
  187. end;
  188. procedure TFPReportConnector.LoadFromConfig(aConfig: TJSONObject);
  189. Var
  190. S : String;
  191. A : TJSONArray;
  192. I : Integer;
  193. begin
  194. ConnectorType:=aConfig.get(keyType,'');
  195. HostName:=aConfig.get(keyHostName,'');
  196. DatabaseName:=aConfig.get(keyDatabaseName,'');
  197. UserName:=aConfig.get(keyUserName,'');
  198. S:=aConfig.get(keyPassword,'');
  199. if (S<>'') then
  200. Password:=XORDecode(keyHash,S);
  201. Role:=aConfig.get(keyRole,'');
  202. Params.Clear;
  203. A:=aConfig.get(keyParams,TJSONArray(Nil));
  204. If Assigned(A) then
  205. For I:=0 to A.Count-1 do
  206. Params.Add(A.Strings[i]);
  207. end;
  208. class function TFPReportConnector.CreateConnection(aConfig: TJSONObject): TFPReportConnector;
  209. begin
  210. Result:=Self.Create(Nil);
  211. Result.LoadFromConfig(aConfig);
  212. Result.LogEvents:=LogAllEventsExtra;
  213. Result.Transaction:=TFPReportSQLtransaction.Create(Result);
  214. end;
  215. class function TFPReportConnector.TestConnection(aConfig: TJSONObject): string;
  216. Var
  217. C : TFPReportConnector;
  218. begin
  219. Result:='';
  220. C:=CreateConnection(aConfig);
  221. try
  222. C.Connected:=True;
  223. except
  224. On E : Exception do
  225. Result:=E.Message;
  226. end;
  227. C.free;
  228. end;
  229. class function TFPReportConnector.CreateDataset(aOwner: TComponent; aConfig: TJSONObject): TSQLQuery;
  230. Var
  231. S : String;
  232. C : TFPReportConnector;
  233. I : integer;
  234. O : TJSONObject;
  235. begin
  236. O:=aConfig.Get(keyConnection,TJSONObject(Nil));
  237. if O=Nil then
  238. Raise EDatabaseError.Create(SErrNoConnectionData);
  239. S:=CreateConfigHash(o);
  240. i:=FPool.IndexOf(S);
  241. if (I<>-1) then
  242. C:=FPool.Objects[i] as TFPReportConnector
  243. else
  244. begin
  245. C:=CreateConnection(o);
  246. FPool.AddObject(S,C);
  247. end;
  248. Result:=TFPReportQuery.Create(aOwner);
  249. Result.Database:=C;
  250. Result.SQL.Text:=aConfig.get(keySQL,'');
  251. // Result.UniDirectional:=True;
  252. Result.PacketRecords:=-1;
  253. Result.UsePrimaryKeyAsKey:=False;
  254. Inc(C.FRefCount);
  255. end;
  256. { TSQLDBReportDataHandler }
  257. function TSQLDBReportDataHandler.CreateDataset(AOwner: TComponent; AConfig: TJSONObject): TDataset;
  258. begin
  259. Result:=TFPReportConnector.CreateDataset(aOwner,aConfig);
  260. end;
  261. class procedure TSQLDBReportDataHandler.StartRender(ADataset: TDataset);
  262. begin
  263. TFPReportConnector.StartRender(aDataset);
  264. end;
  265. class procedure TSQLDBReportDataHandler.EndRender(ADataset: TDataset);
  266. begin
  267. TFPReportConnector.EndRender(aDataset);
  268. end;
  269. class function TSQLDBReportDataHandler.CheckConfig(AConfig: TJSONObject): String;
  270. Var
  271. O : TJSONObject;
  272. begin
  273. O:=aConfig.Get(keyConnection,TJSONObject(Nil));
  274. if (O=Nil) or (O.Count=0) then
  275. Result:=SErrNoConnectionData
  276. else if Trim(aConfig.Get(keySQL,''))='' then
  277. Result:=SErrNoSQL
  278. end;
  279. class function TSQLDBReportDataHandler.DataType: String;
  280. begin
  281. Result:='SQLDB';
  282. end;
  283. class function TSQLDBReportDataHandler.DataTypeDescription: String;
  284. begin
  285. Result:='SQL Database server';
  286. end;
  287. class function TSQLDBReportDataHandler.AllowMasterDetail: Boolean;
  288. begin
  289. Result:=True;
  290. end;
  291. class procedure TSQLDBReportDataHandler.SetMasterDataset(ADetail, AMaster: TDataset);
  292. Var
  293. Q : TSQLQuery;
  294. DS : TDatasource;
  295. begin
  296. Q:=(ADetail as TSQLQuery);
  297. DS:=Q.DataSource;
  298. if DS=Nil then
  299. begin
  300. DS:=TDatasource.Create(Q);
  301. Q.Datasource:=DS;
  302. end;
  303. DS.Dataset:=AMaster;
  304. end;
  305. initialization
  306. TSQLDBReportDataHandler.RegisterHandler;
  307. TFPReportConnector.Init;
  308. Finalization
  309. TFPReportConnector.Done;
  310. end.