123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385 |
- {
- This file is part of the Free Component Library.
- Copyright (c) 2017 Michael Van Canneyt, member of the Free Pascal development team
- Report Designer Data connector for SQLDB based data.
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- **********************************************************************}
- unit fpreportdatasqldb;
- {$mode objfpc}{$H+}
- interface
- uses
- Classes, SysUtils, strutils, sqldb, db, fpjson, fpreportdata;
- Const
- keyConnection = 'connection';
- keySQL = 'sql';
- keyType = 'dbtype';
- keyHostName = 'host';
- keyDatabaseName = 'database';
- keyUserName = 'user';
- keyPassword = 'pwd';
- keyRole = 'role';
- keyParams = 'params';
- KeyCharSet = 'charset';
- keyHash = 'FPCRulez';
- Resourcestring
- SErrNoConnectionData = 'No connection data available';
- SErrNoSQL = 'No SQL statement set';
- Type
- { TFPReportConnector }
- TFPReportConnector = Class(TSQLConnector)
- Private
- FRefCount: Integer;
- Class procedure init;
- class procedure done;
- Class var
- FPool : TStringList;
- Public
- Procedure LoadFromConfig(aConfig : TJSONObject);
- class function CreateConnection(aConfig: TJSONObject): TFPReportConnector; virtual;
- Class Function TestConnection (aConfig : TJSONObject) : string; virtual;
- class function CreateDataset(aOwner: TComponent; aConfig: TJSONObject): TSQLQuery;
- class function CreateConfigHash(aConfig: TJSONObject): String;
- Class Procedure StartRender(ADataset : TDataset); virtual;
- Class Procedure EndRender(ADataset : TDataset); virtual;
- Class procedure CheckDBRelease;
- Property RefCount : Integer Read FRefCount;
- end;
- { TFPReportQuery }
- TFPReportQuery = class(TSQLQuery)
- Public
- Constructor Create(AOwner : TComponent); override;
- Destructor Destroy; override;
- end;
- { TReportSQLtransaction }
- TFPReportSQLtransaction = Class(TSQLTransaction)
- private
- FStartRefCount: Integer;
- Public
- Procedure StartRender;
- Procedure EndRender;
- Property StartRefCount : Integer Read FStartRefCount;
- end;
- { TSQLDBReportDataHandler }
- TSQLDBReportDataHandler = Class(TFPReportDataHandler)
- Function CreateDataset(AOwner : TComponent; AConfig : TJSONObject) : TDataset; override;
- Class Procedure StartRender(ADataset : TDataset); override;
- Class Procedure EndRender(ADataset : TDataset); override;
- Class Function CheckConfig(AConfig: TJSONObject): String; override;
- Class Function DataType : String; override;
- Class Function DataTypeDescription : String; override;
- Class Function AllowMasterDetail: Boolean; override;
- Class Procedure SetMasterDataset(ADetail, AMaster: TDataset); override;
- end;
- implementation
- { TFPReportSQLtransaction }
- procedure TFPReportSQLtransaction.StartRender;
- Var
- Start : Boolean;
- begin
- Start:=(FStartRefCount=0);
- Inc(FStartRefCount);
- if Start and not Active then
- StartTransaction;
- end;
- procedure TFPReportSQLtransaction.EndRender;
- begin
- if FStartRefCount>0 then
- begin
- Dec(FStartRefCount);
- If FStartRefCount=0 then
- RollBack;
- end;
- end;
- { TFPReportQuery }
- constructor TFPReportQuery.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- ReadOnly:=True;
- end;
- destructor TFPReportQuery.Destroy;
- begin
- If Database is TFPReportConnector then
- Dec(TFPReportConnector(Database).FRefCount);
- inherited Destroy;
- TFPReportConnector.CheckDBRelease;
- end;
- { TFPReportConnector }
- class procedure TFPReportConnector.init;
- begin
- FPool:=TStringList.Create;
- FPool.OwnsObjects:=True;
- FPool.Sorted:=True;
- FPool.Duplicates:=dupError;
- end;
- class procedure TFPReportConnector.done;
- begin
- FreeAndNil(FPool);
- end;
- Class Function TFPReportConnector.CreateConfigHash(aConfig : TJSONObject) : String;
- Procedure AH(N,V : String);
- begin
- if (V<>'') then
- Result:=Result+';'+N+'='+V;
- end;
- Procedure AH(N : String);
- begin
- AH(N,aConfig.get(N,''));
- end;
- Var
- A : TJSONArray;
- I : Integer;
- begin
- AH(keyType);
- AH(keyHostName);
- AH(keyDatabaseName);
- AH(keyUserName);
- AH(keyPassword);
- AH(keyRole);
- A:=aConfig.get(keyParams,TJSONArray(Nil));
- If Assigned(A) then
- For I:=0 to A.Count-1 do
- AH(IntToStr(I),A.Strings[i]);
- end;
- class procedure TFPReportConnector.StartRender(ADataset: TDataset);
- var
- Q : TFPReportQuery;
- T : TFPReportSQLTransaction;
- begin
- if (aDataset is TFPReportQuery) then
- begin
- Q:=aDataset as TFPReportQuery;
- if Q.Transaction is TFPReportSQLTransaction then
- begin
- T:=Q.Transaction as TFPReportSQLTransaction;
- T.StartRender;
- end;
- end;
- end;
- class procedure TFPReportConnector.EndRender(ADataset: TDataset);
- var
- Q : TFPReportQuery;
- T : TFPReportSQLTransaction;
- begin
- if (aDataset is TFPReportQuery) then
- begin
- Q:=aDataset as TFPReportQuery;
- if Q.Transaction is TFPReportSQLTransaction then
- begin
- T:=Q.Transaction as TFPReportSQLTransaction;
- T.EndRender;
- end;
- end;
- end;
- class procedure TFPReportConnector.CheckDBRelease;
- Var
- I : Integer;
- begin
- For I:=FPool.Count-1 downto 0 do
- begin
- // Writeln('Connection count for ',FPool[i], ' : ',TFPReportConnector(FPool.Objects[i]).FRefCount);
- if TFPReportConnector(FPool.Objects[i]).FRefCount=0 then
- FPool.Delete(I);
- end;
- end;
- procedure TFPReportConnector.LoadFromConfig(aConfig: TJSONObject);
- Var
- S : String;
- A : TJSONArray;
- I : Integer;
- begin
- ConnectorType:=aConfig.get(keyType,'');
- HostName:=aConfig.get(keyHostName,'');
- DatabaseName:=aConfig.get(keyDatabaseName,'');
- UserName:=aConfig.get(keyUserName,'');
- S:=aConfig.get(keyPassword,'');
- if (S<>'') then
- Password:=XORDecode(keyHash,S);
- Role:=aConfig.get(keyRole,'');
- Params.Clear;
- A:=aConfig.get(keyParams,TJSONArray(Nil));
- If Assigned(A) then
- For I:=0 to A.Count-1 do
- Params.Add(A.Strings[i]);
- end;
- class function TFPReportConnector.CreateConnection(aConfig: TJSONObject): TFPReportConnector;
- begin
- Result:=Self.Create(Nil);
- Result.LoadFromConfig(aConfig);
- Result.LogEvents:=LogAllEventsExtra;
- Result.Transaction:=TFPReportSQLtransaction.Create(Result);
- end;
- class function TFPReportConnector.TestConnection(aConfig: TJSONObject): string;
- Var
- C : TFPReportConnector;
- begin
- Result:='';
- C:=CreateConnection(aConfig);
- try
- C.Connected:=True;
- except
- On E : Exception do
- Result:=E.Message;
- end;
- C.free;
- end;
- class function TFPReportConnector.CreateDataset(aOwner: TComponent; aConfig: TJSONObject): TSQLQuery;
- Var
- S : String;
- C : TFPReportConnector;
- I : integer;
- O : TJSONObject;
- begin
- O:=aConfig.Get(keyConnection,TJSONObject(Nil));
- if O=Nil then
- Raise EDatabaseError.Create(SErrNoConnectionData);
- S:=CreateConfigHash(o);
- i:=FPool.IndexOf(S);
- if (I<>-1) then
- C:=FPool.Objects[i] as TFPReportConnector
- else
- begin
- C:=CreateConnection(o);
- FPool.AddObject(S,C);
- end;
- Result:=TFPReportQuery.Create(aOwner);
- Result.Database:=C;
- Result.SQL.Text:=aConfig.get(keySQL,'');
- // Result.UniDirectional:=True;
- Result.PacketRecords:=-1;
- Result.UsePrimaryKeyAsKey:=False;
- Inc(C.FRefCount);
- end;
- { TSQLDBReportDataHandler }
- function TSQLDBReportDataHandler.CreateDataset(AOwner: TComponent; AConfig: TJSONObject): TDataset;
- begin
- Result:=TFPReportConnector.CreateDataset(aOwner,aConfig);
- end;
- class procedure TSQLDBReportDataHandler.StartRender(ADataset: TDataset);
- begin
- TFPReportConnector.StartRender(aDataset);
- end;
- class procedure TSQLDBReportDataHandler.EndRender(ADataset: TDataset);
- begin
- TFPReportConnector.EndRender(aDataset);
- end;
- class function TSQLDBReportDataHandler.CheckConfig(AConfig: TJSONObject): String;
- Var
- O : TJSONObject;
- begin
- O:=aConfig.Get(keyConnection,TJSONObject(Nil));
- if (O=Nil) or (O.Count=0) then
- Result:=SErrNoConnectionData
- else if Trim(aConfig.Get(keySQL,''))='' then
- Result:=SErrNoSQL
- end;
- class function TSQLDBReportDataHandler.DataType: String;
- begin
- Result:='SQLDB';
- end;
- class function TSQLDBReportDataHandler.DataTypeDescription: String;
- begin
- Result:='SQL Database server';
- end;
- class function TSQLDBReportDataHandler.AllowMasterDetail: Boolean;
- begin
- Result:=True;
- end;
- class procedure TSQLDBReportDataHandler.SetMasterDataset(ADetail, AMaster: TDataset);
- Var
- Q : TSQLQuery;
- DS : TDatasource;
- begin
- Q:=(ADetail as TSQLQuery);
- DS:=Q.DataSource;
- if DS=Nil then
- begin
- DS:=TDatasource.Create(Q);
- Q.Datasource:=DS;
- end;
- DS.Dataset:=AMaster;
- end;
- initialization
- TSQLDBReportDataHandler.RegisterHandler;
- TFPReportConnector.Init;
- Finalization
- TFPReportConnector.Done;
- end.
|