unit sqldbwebdata; {$mode objfpc}{$H+} interface uses Classes, SysUtils, fphttp, fpwebdata, DB, SQLDB; Type { TCustomSQLDBWebDataProvider } TNewIDEvent = Procedure(Sender : TObject; Out AID : String) of object; TGetParamTypeEvent = Procedure (Sender : TObject; Const ParamName,AValue : String; Var AType : TFieldtype) of object; TGetParamValueEvent = Procedure (Sender : TObject; P : TParam; Var Handled : Boolean) of object; TCustomSQLDBWebDataProvider = Class(TFPCustomWebDataProvider) private FIDFieldName: String; FONGetDataset: TNotifyEvent; FOnGetNewID: TNewIDEvent; FOnGetParamValue: TGetParamValueEvent; FParams: TParams; FSQLS : Array[0..3] of TStringList; FConnection: TSQLConnection; FQuery : TSQLQuery; FLastNewID : String; FOnGetParamType : TGetParamTypeEvent; function GetS(AIndex: integer): TStrings; procedure RegenerateParams; procedure SetConnection(const AValue: TSQLConnection); procedure SetParams(const AValue: TParams); procedure SetS(AIndex: integer; const AValue: TStrings); Protected function CheckDataset : Boolean; virtual; function CreateQuery(AOwner: TComponent; ATransaction: TSQLTransaction; ASQL: Tstrings): TSQLQuery; function GetParamType(P: TParam; const AValue: String): TFieldType; virtual; procedure SetTypedParam(P: TParam; Const AValue: String); virtual; procedure ExecuteSQL(ASQL: TStrings; Msg: String=''; DoNewID : Boolean = False); virtual; procedure ApplySQLParams(AQuery: TSQLQuery; DoNewID : Boolean = False); virtual; Procedure SQLChanged(Sender : TObject); virtual; Procedure DoUpdate; override; Procedure DoDelete; override; Procedure DoInsert; override; Procedure DoApplyParams; override; Function SQLQuery : TSQLQuery; Function GetDataset : TDataset; override; Function DoGetNewID : String; virtual; Function GetNewID : String; Function IDFieldValue : String; override; procedure Notification(AComponent: TComponent; Operation: TOperation); override; Property SelectSQL : TStrings Index 0 Read GetS Write SetS; Property UpdateSQL : TStrings Index 1 Read GetS Write SetS; Property DeleteSQL : TStrings Index 2 Read GetS Write SetS; Property InsertSQL : TStrings Index 3 Read GetS Write SetS; Property Connection : TSQLConnection Read FConnection Write SetConnection; Property OnGetNewID : TNewIDEvent Read FOnGetNewID Write FOnGetNewID; property OnGetParameterType : TGetParamTypeEvent Read FOnGetParamType Write FOnGetParamType; property OnGetParameterValue : TGetParamValueEvent Read FOnGetParamValue Write FOnGetParamValue; Property OnGetDataset : TNotifyEvent Read FONGetDataset Write FOnGetDataset; Property Params : TParams Read FParams Write SetParams; Public Constructor Create(AOwner : TComponent); override; Destructor Destroy; override; end; TSQLDBWebDataProvider = Class(TCustomSQLDBWebDataProvider) Published Property SelectSQL; Property UpdateSQL; Property DeleteSQL; Property InsertSQL; Property Connection; Property IDFieldName; Property OnGetNewID; property OnGetParameterType; property OnGetParameterValue; Property OnGetDataset; Property Options; Property Params; end; implementation { $define wmdebug} {$ifdef wmdebug} uses dbugintf; {$endif} resourcestring SErrNoSelectSQL = '%s: No select SQL statement provided.'; SErrNoUpdateSQL = '%s: No update SQL statement provided.'; SErrNoInsertSQL = '%s: No insert SQL statement provided.'; SErrNoDeleteSQL = '%s: No delete SQL statement provided.'; SErrUpdating = '%s: An error occurred during the update operation: %s'; SErrDeleting = '%s: An error occurred during the delete operation: %s'; SErrInserting = '%s: An error occurred during the insert operation: %s'; SErrNoNewIDEvent = '%s : Cannot generate ID: No OnGetNewID event assigned.'; { TCustomSQLDBWebDataProvider } function TCustomSQLDBWebDataProvider.GetS(AIndex: integer): TStrings; begin Result:=FSQLS[AIndex]; end; procedure TCustomSQLDBWebDataProvider.SetConnection(const AValue: TSQLConnection ); begin if (FConnection=AValue) then exit; If Assigned(FConnection) then FConnection.RemoveFreeNotification(Self); FConnection:=AValue; If Assigned(FConnection) then FConnection.FreeNotification(Self); end; procedure TCustomSQLDBWebDataProvider.SetParams(const AValue: TParams); begin if FParams=AValue then exit; FParams.Assign(AValue); end; procedure TCustomSQLDBWebDataProvider.SetS(AIndex: integer; const AValue: TStrings); begin FSQLS[AIndex].Assign(AValue); end; procedure TCustomSQLDBWebDataProvider.SQLChanged(Sender: TObject); begin If (Sender=SelectSQL) then begin if Assigned(FQuery) then begin FQuery.Close; FQuery.SQL.Assign(SelectSQL); end; If Not (csLoading in ComponentState) then RegenerateParams; end; end; procedure TCustomSQLDBWebDataProvider.RegenerateParams; Var S : String; begin S:=SelectSQL.Text; Params.Clear; Params.ParseSQL(S,True); end; procedure TCustomSQLDBWebDataProvider.ExecuteSQL(ASQL : TStrings; Msg : String = ''; DoNewID : Boolean = False); Var Q : TSQLQuery; begin {$ifdef wmdebug}SendDebug('Entering TCustomSQLDBWebDataProvider.ExecuteSQL');{$endif} Q:=CreateQuery(Nil,Nil,ASQL); try Q.Transaction.Active:=True; try ApplySQLParams(Q,DoNewID); Q.ExecSQL; (Q.Transaction as TSQLTransaction).Commit; except On E : Exception do begin (Q.Transaction as TSQLTransaction).Rollback; If (Msg<>'') then E.Message:=Format(Msg,[Self.Name,E.Message]); Raise; end; end finally Q.Free; end; {$ifdef wmdebug}SendDebug('Exiting TCustomSQLDBWebDataProvider.ExecuteSQL');{$endif} end; procedure TCustomSQLDBWebDataProvider.DoUpdate; begin {$ifdef wmdebug}SendDebug('Entering TCustomSQLDBWebDataProvider.DoUpdate');{$endif} If (Trim(UpdateSQL.Text)='') then Raise EFPHTTPError.CreateFmt(SErrNoUpdateSQL,[Self.Name]); FLastNewID:=''; ExecuteSQL(UpdateSQL,SErrUpdating); {$ifdef wmdebug}SendDebug('Exiting TCustomSQLDBWebDataProvider.DoUpdate');{$endif} end; procedure TCustomSQLDBWebDataProvider.DoDelete; begin {$ifdef wmdebug}SendDebug('Entering TCustomSQLDBWebDataProvider.DoDelete');{$endif} If (Trim(DeleteSQL.Text)='') then Raise EFPHTTPError.CreateFmt(SErrNoDeleteSQL,[Self.Name]); FLastNewID:=''; ExecuteSQL(DeleteSQL,SErrDeleting); {$ifdef wmdebug}SendDebug('Exiting TCustomSQLDBWebDataProvider.DoDelete');{$endif} end; procedure TCustomSQLDBWebDataProvider.DoInsert; begin {$ifdef wmdebug}SendDebug('Entering TCustomSQLDBWebDataProvider.DoInsert');{$endif} If (Trim(InsertSQL.Text)='') then Raise EFPHTTPError.CreateFmt(SErrNoInsertSQL,[Self.Name]); FLastNewID:=''; ExecuteSQL(InsertSQL,SErrInserting,(IDFieldName<>'')); {$ifdef wmdebug}SendDebug('Exiting TCustomSQLDBWebDataProvider.DoInsert');{$endif} end; procedure TCustomSQLDBWebDataProvider.Notification(AComponent: TComponent; Operation: TOperation); begin inherited; If (Operation=opRemove) then begin If (AComponent=FQuery) then FQuery:=Nil else if (AComponent=FConnection) then FConnection:=Nil; end; end; Function TCustomSQLDBWebDataProvider.CreateQuery(AOwner : TComponent; ATransaction : TSQLTransaction; ASQL : Tstrings) : TSQLQuery; begin Result:=TSQLQuery.Create(AOwner); If (AOwner<>Self) then Result.FreeNotification(Self); Result.DataBase:=Connection; If ATransaction=Nil then begin ATransaction:=TSQLTransaction.Create(Result); ATransaction.DataBase:=Connection; end; Result.Transaction:=ATransaction; Result.SQL.Assign(ASQL); end; Function TCustomSQLDBWebDataProvider.CheckDataset : boolean; begin {$ifdef wmdebug}SendDebug('Entering CheckDataset');{$endif} If (Trim(SelectSQL.Text)='') then Raise EFPHTTPError.CreateFmt(SErrNoSelectSQL,[Self.Name]); Result:=FQuery=Nil; If (Result) then FQuery:=CreateQuery(Nil,Nil,SelectSQL) else if not FQuery.Active then FQuery.SQL.Assign(SelectSQL); {$ifdef wmdebug}SendDebug('Exiting CheckDataset');{$endif} end; Function TCustomSQLDBWebDataProvider.GetParamType(P : TParam; Const AValue : String) : TFieldType; begin Result:=ftunknown; If Assigned(FOnGetParamType) then FOnGetParamType(Self,P.Name,AValue,Result); end; procedure TCustomSQLDBWebDataProvider.SetTypedParam(P : TParam; Const AValue : String); Var I : Integer; Q : Int64; D : TDateTime; ft : TFieldType; F : Double; B : Boolean; C : Currency; begin ft:=GetParamtype(P,AValue); If (AValue='') and (not (ft in [ftString,ftFixedChar,ftWideString,ftFixedWideChar])) then begin P.Clear; exit; end; If (ft<>ftUnknown) then begin try case ft of ftInteger, ftword, ftsmallint : I:=StrToInt(AValue); ftDate : D:=StrToDate(AValue); ftDateTime, ftTimestamp : D:=StrToDateTime(AValue); ftBoolean : B:=StrToBool(AValue); ftTime : D:=StrToTime(AValue); ftLargeint : Q:=StrToInt64(AValue); ftCurrency : C:=StrToCurr(Avalue); else ft:=ftString end except ft:=ftUnknown end; end; If (ft=ftUnknown) and (Length(AValue)<30) then begin if TryStrToInt(Avalue,I) then ft:=ftInteger else if TryStrToInt64(Avalue,Q) then ft:=ftInteger else if (Pos(DateSeparator,AValue)<>0) then begin if (Pos(TimeSeparator,AValue)<>0) and TryStrToDateTime(Avalue,D) then ft:=ftDateTime else if TryStrToDate(Avalue,D) then ft:=ftDate end else If (Pos(TimeSeparator,AValue)<>0) and TryStrToTime(Avalue,D) then ft:=ftTime else if (Pos(DecimalSeparator,AValue)<>0) then begin if trystrtofloat(AValue,F) then ft:=ftFloat else if TryStrToCurr(Avalue,C) then ft:=ftCurrency end else if TryStrToBool(Avalue,B) then ft:=ftBoolean end; Case ft of ftInteger, ftword, ftsmallint : P.AsInteger:=I; ftBoolean : P.AsBoolean:=B; ftLargeInt : P.AsLargeInt:=Q; ftDate : P.AsDate:=D; ftDateTime, ftTimestamp : P.AsDateTime:=D; ftTime : P.AsTime:=D; ftFloat, ftBCD, ftFMTBCD : P.AsFloat:=F; ftCurrency : P.AsCurrency:=F; else P.AsString:=AValue; end; end; procedure TCustomSQLDBWebDataProvider.ApplySQLParams(AQuery : TSQLQuery; DoNewID : Boolean = False); function TryAdaptor (const aName: string; P: TParam) : boolean; var S : string; begin result := Adaptor.TryFieldValue(aName,S); if not result then result := Adaptor.TryParamValue(aName,S); if result then SetTypedParam(P,S); end; var I: Integer; P : TParam; S : String; B : Boolean; begin {$ifdef wmdebug}SendDebug('Entering ApplySQLPArams');{$endif} For I:=0 to AQuery.Params.Count-1 do begin P:=AQuery.Params[i]; B:=Assigned(FOnGetParamValue); if B then FOnGetParamValue(Self,P,B); if not B then begin If (P.Name=IDFieldName) then begin if DoNewID then begin GetNewID; SetTypedParam(P,FLastNewID) end else begin if not TryAdaptor (P.Name, P) then TryAdaptor('ID', P); end; end else if not TryAdaptor (P.Name, P) then P.Clear; end; end; {$ifdef wmdebug}SendDebug('Exiting ApplySQLPArams');{$endif} end; procedure TCustomSQLDBWebDataProvider.DoApplyParams; begin CheckDataset; ApplySQLParams(FQuery); end; function TCustomSQLDBWebDataProvider.SQLQuery: TSQLQuery; begin Result:=FQuery; end; function TCustomSQLDBWebDataProvider.GetDataset: TDataset; begin {$ifdef wmdebug}SendDebug('Get dataset: checking dataset');{$endif} If Assigned(FonGetDataset) then FOnGetDataset(Self); CheckDataset; FLastNewID:=''; Result:=FQuery; {$ifdef wmdebug}SendDebug('Get dataset: activating transaction');{$endif} If Not FQuery.Transaction.Active then FQuery.Transaction.Active:=True; {$ifdef wmdebug}SendDebug('Get dataset: done');{$endif} end; function TCustomSQLDBWebDataProvider.DoGetNewID: String; begin If Not Assigned(FOnGetNewID) then Raise EFPHTTPError.CreateFmt(SErrNoNewIDEvent,[Self.Name]); FOnGetNewID(Self,Result); end; function TCustomSQLDBWebDataProvider.GetNewID: String; begin Result:=DoGetNewID; FLastNewID:=Result; end; function TCustomSQLDBWebDataProvider.IDFieldValue: String; begin {$ifdef wmdebug}SendDebug('Entering IDFieldValue');{$endif} If (FLastNewID<>'') then Result:=FLastNewID else If (IDFieldName<>'') then begin If not Adaptor.TryParamValue(IDFieldName,Result) then If not Adaptor.TryFieldValue(IDFieldName,Result) then Result:=inherited IDFieldValue; end else Result:=inherited IDFieldValue; {$ifdef wmdebug}SendDebug('Exiting IDFieldValue : '+Result);{$endif} end; constructor TCustomSQLDBWebDataProvider.Create(AOwner: TComponent); Var I : Integer; L : TStringList; begin inherited Create(AOwner); For I:=0 to 3 do begin L:=TStringList.Create; L.OnChange:=@SQLChanged; FSQLS[i]:=L; end; FParams:=TParams.Create(TParam); end; destructor TCustomSQLDBWebDataProvider.Destroy; Var I: Integer; begin For I:=0 to 3 do FreeAndNil(FSQLS[i]); Connection:=Nil; FreeAndNil(FQuery); FreeAndNil(FParams); inherited Destroy; end; end.