unit DataAbstractService_Impl; {----------------------------------------------------------------------------} { Data Abstract Library - Core Library } { } { compiler: Delphi 6 and up, Kylix 3 and up } { platform: Win32, Linux } { } { (c)opyright RemObjects Software. all rights reserved. } { } { Using this code requires a valid license of the Data Abstract } { which can be obtained at http://www.remobjects.com. } {----------------------------------------------------------------------------} {----------------------------------------------------------------------------} { When applying fixes to this unit, please see if the need to be propagates } { to the duplicate dlogic in legacy DARemoteService_Impl.pas, too. } {----------------------------------------------------------------------------} {$I DataAbstract.inc} interface uses {vcl:} Classes, SysUtils, DB, {Generated:} DataAbstract4_Intf, uROClientIntf, uROTypes, uROServer, uROSessions, uRORemoteDataModule, uROClasses, uDAInterfaces, uDAClasses, uDADataTable, uDADataStreamer, uDABusinessProcessor, uDADataTableReferenceCollection, uDADelta; type { Events } TDAAcquireConnectionEvent = procedure(aSender: TObject; var aConnectionName: string) of object; TDAConnectionAcquiredEvent = procedure(aSender: TObject; const aConnectionName: string; const aAcquiredConnection: IDAConnection) of object; TDAAcquireConnectionFailureEvent = procedure(aSender: TObject; const aConnectionName: string; aError: Exception) of object; TDAGetDatasetSchemaEvent = procedure(aSender: TObject; const aDataset: IDADataset) of object; TDAGetDatasetDataEvent = procedure(aSender: TObject; const aDataset: IDADataset; const aIncludeSchema: Boolean; const aMaxRecords: Integer) of object; TDABusinessProcessorAutoCreatedEvent = procedure(aSender: TRORemoteDataModule; BusinessProcessor : TDABusinessProcessor) of object; TDABeforeExecuteCommandEvent = procedure(aSender: TObject; const aCommand: IDASQLCommand) of object; TDAAfterExecuteCommandEvent = procedure(aSender: TObject; const aCommand: IDASQLCommand; aRowsAffacted : integer) of object; TDAConnectionReleasedEvent = procedure(aSender: TObject; const aConnectionName: string) of object; TDAGetSchemaAsXMLEvent = procedure(aSender: TObject; var aSchemaXML: Utf8string) of object; TDAProcessDeltasEvent = procedure(aSender: TObject; aDeltaStructs: TDADeltaStructList) of object; TDAProcessDeltasErrorEvent = procedure(aSender: TObject; aDeltaStructs: TDADeltaStructList; aError: Exception; var aDoRaise: boolean) of object; TDASchemaElementAccessValidationEvent = procedure(Sender: TObject; const aConnection: IDAConnection; const aDatasetName: string; const aParamNames: array of string; const aParamValues : array of variant; aSchema: TDASchema; var Allowed : boolean) of object; TDASQLValidationEvent = procedure(Sender: TObject; const aConnection: IDAConnection; const aSQLText: string; const aParamNames: array of string; const aParamValues : array of variant; var Allowed : boolean) of object; TDAUpdateDataTransactionEvent = procedure(Sender: TObject; var aUseDefaultTransactionLogic: Boolean) of object; TDADeltasMode = (dumExported, dumStandard); TDADeltasModes = set of TDADeltasMode; { TDataAbstractService } TDataAbstractService = class(TRORemoteDataModule, IDataAbstractService) private { Properties } fAllowExecuteCommands: boolean; fAllowExecuteSQL: boolean; fAllowDataAccess: boolean; fAllowSchemaAccess: boolean; fAllowWhereSQL: boolean; fConnectionName: string; fAcquireConnection: boolean; fServiceSchema: TDASchema; fAutoCreateBusinessProcessors: boolean; fServiceDataStreamer: TDADataStreamer; fProcessDeltasWithoutUpdateRules: boolean; fConnection: IDAConnection; fHETConnection: IDAHETConnection; fExportedDataTables: TDADataTableReferenceCollection; { Events } fBeforeGetDatasetSchema: TDAGetDatasetSchemaEvent; fBeforeProcessDeltas: TDAProcessDeltasEvent; fOnAcquireConnectionFailure: TDAAcquireConnectionFailureEvent; fOnUpdateDataCommitTransaction: TDAUpdateDataTransactionEvent; fGetDatasetDataValidation: TDASchemaElementAccessValidationEvent; fExecuteCommandValidation: TDASchemaElementAccessValidationEvent; fSQLValidation: TDASQLValidationEvent; fAfterExecuteCommand: TDAAfterExecuteCommandEvent; fBeforeAcquireConnection: TDAAcquireConnectionEvent; fOnUpdateDataRollBackTransaction: TDAUpdateDataTransactionEvent; fAfterGetDatasetData: TDAGetDatasetDataEvent; fAfterReleaseConnection: TDAConnectionReleasedEvent; fAfterGetDatasetSchema: TDAGetDatasetSchemaEvent; fAfterProcessDeltas: TDAProcessDeltasEvent; fOnGetSchemaAsXML: TDAGetSchemaAsXMLEvent; fOnBusinessProcessorAutoCreated: TDABusinessProcessorAutoCreatedEvent; fOnUpdateDataBeginTransaction: TDAUpdateDataTransactionEvent; fAfterAcquireConnection: TDAConnectionAcquiredEvent; fOnProcessDeltasError: TDAProcessDeltasErrorEvent; fBeforeExecuteCommand: TDABeforeExecuteCommandEvent; fBeforeGetDatasetData: TDAGetDatasetDataEvent; fBeforeReleaseConnection: TDAConnectionAcquiredEvent; fAllowDynamicSelect: boolean; fAllowDynamicWhere: boolean; fAllowUpdates: boolean; fHasReducedDelta: Boolean; procedure SetServiceDataStreamer(const Value: TDADataStreamer); procedure SetServiceSchema(const Value: TDASchema); procedure SetExportedDataTables(const Value: TDADataTableReferenceCollection); procedure Check(CheckOnlyDataStreamer:Boolean = false); private function GetConnection: IDAConnection; function UnpackDeltas(const DeltaStream: Binary; DeltaStructList: TDADeltaStructList): TDADeltasModes; function TriggerTransactionEvent(aEvent: TDAUpdateDataTransactionEvent): boolean; procedure ExportedDataTables_MergeDelta(ADelta: IDADelta); protected { IDataAbstractService methods } function GetSchema(const aFilter: Utf8String): Utf8String; function GetData(const aTableNameArray: StringArray; const aTableRequestInfoArray: TableRequestInfoArray): Binary; function UpdateData(const aDelta: Binary): Binary; function ExecuteCommand(const aCommandName: Utf8String; const aParameterArray: DataParameterArray): Integer; function GetTableSchema(const aTableNameArray: StringArray): Utf8String; function GetCommandSchema(const aCommandNameArray: StringArray): Utf8String; function SQLGetData(const aSQLText: Utf8String; const aIncludeSchema: Boolean; const aMaxRecords: Integer): Binary; function SQLExecuteCommand(const aSQLText: Utf8String): Integer; function ExecuteCommandEx(const aCommandName: Utf8String; const aInputParameters: DataParameterArray; out aOutputParameters: DataParameterArray): Integer; function GetDatasetScripts(const DatasetNames: Utf8String): Utf8String; procedure RegisterForDataChangeNotification(const aTableName: Utf8String); procedure UnregisterForDataChangeNotification(const aTableName: Utf8String); { TRORemoteDataModule } procedure DoOnDeactivate(aClientID: TGUID); override; procedure Notification(AComponent: TComponent; Operation: TOperation); override; function GetConnectionForObject(const aName: string): IDAConnection; virtual; public constructor Create(aOwner : TComponent); override; destructor Destroy; override; procedure SetConnection(aConnection: IDAConnection); deprecated; {$WARN SYMBOL_DEPRECATED OFF} property Connection: IDAConnection read GetConnection write SetConnection; {$WARN SYMBOL_DEPRECATED ON} procedure ReleaseConnection; published property AcquireConnection: boolean read fAcquireConnection write fAcquireConnection default true; property ConnectionName: string read fConnectionName write fConnectionName; property AutoCreateBusinessProcessors : boolean read fAutoCreateBusinessProcessors write fAutoCreateBusinessProcessors default true; property ServiceSchema: TDASchema read fServiceSchema write SetServiceSchema; property ServiceDataStreamer: TDADataStreamer read fServiceDataStreamer write SetServiceDataStreamer; property AllowDataAccess: boolean read fAllowDataAccess write fAllowDataAccess default true; property AllowSchemaAccess: boolean read fAllowSchemaAccess write fAllowSchemaAccess default true; property AllowUpdates: boolean read fAllowUpdates write fAllowUpdates default true; property AllowExecuteSQL: boolean read fAllowExecuteSQL write fAllowExecuteSQL default false; property AllowWhereSQL: boolean read fAllowWhereSQL write fAllowWhereSQL default false; property AllowExecuteCommands: boolean read fAllowExecuteCommands write fAllowExecuteCommands default false; property AllowDynamicSelect: boolean read fAllowDynamicSelect write fAllowDynamicSelect default true; property AllowDynamicWhere: boolean read fAllowDynamicWhere write fAllowDynamicWhere default true; property ProcessDeltasWithoutUpdateRules: boolean read fProcessDeltasWithoutUpdateRules write fProcessDeltasWithoutUpdateRules default true; property ExportedDataTables: TDADataTableReferenceCollection read fExportedDataTables write SetExportedDataTables; { Events } property BeforeAcquireConnection: TDAAcquireConnectionEvent read fBeforeAcquireConnection write fBeforeAcquireConnection; property AfterAcquireConnection: TDAConnectionAcquiredEvent read fAfterAcquireConnection write fAfterAcquireConnection; property BeforeReleaseConnection: TDAConnectionAcquiredEvent read fBeforeReleaseConnection write fBeforeReleaseConnection; property AfterReleaseConnection: TDAConnectionReleasedEvent read fAfterReleaseConnection write fAfterReleaseConnection; property OnAcquireConnectionFailure: TDAAcquireConnectionFailureEvent read fOnAcquireConnectionFailure write fOnAcquireConnectionFailure; property BeforeProcessDeltas : TDAProcessDeltasEvent read fBeforeProcessDeltas write fBeforeProcessDeltas; property AfterProcessDeltas: TDAProcessDeltasEvent read fAfterProcessDeltas write fAfterProcessDeltas; property OnProcessDeltasError: TDAProcessDeltasErrorEvent read fOnProcessDeltasError write fOnProcessDeltasError; property BeforeGetDatasetSchema: TDAGetDatasetSchemaEvent read fBeforeGetDatasetSchema write fBeforeGetDatasetSchema; property BeforeGetDatasetData: TDAGetDatasetDataEvent read fBeforeGetDatasetData write fBeforeGetDatasetData; property AfterGetDatasetSchema: TDAGetDatasetSchemaEvent read fAfterGetDatasetSchema write fAfterGetDatasetSchema; property AfterGetDatasetData: TDAGetDatasetDataEvent read fAfterGetDatasetData write fAfterGetDatasetData; property OnBusinessProcessorAutoCreated: TDABusinessProcessorAutoCreatedEvent read fOnBusinessProcessorAutoCreated write fOnBusinessProcessorAutoCreated; property BeforeExecuteCommand: TDABeforeExecuteCommandEvent read fBeforeExecuteCommand write fBeforeExecuteCommand; property AfterExecuteCommand: TDAAfterExecuteCommandEvent read fAfterExecuteCommand write fAfterExecuteCommand; property OnGetSchemaAsXMLEvent: TDAGetSchemaAsXMLEvent read fOnGetSchemaAsXML write fOnGetSchemaAsXML; property ValidateDatasetAccess: TDASchemaElementAccessValidationEvent read fGetDatasetDataValidation write fGetDatasetDataValidation; property ValidateCommandExecution: TDASchemaElementAccessValidationEvent read fExecuteCommandValidation write fExecuteCommandValidation; property ValidateDirectSQLAccess: TDASQLValidationEvent read fSQLValidation write fSQLValidation; property OnUpdateDataBeginTransaction : TDAUpdateDataTransactionEvent read fOnUpdateDataBeginTransaction write fOnUpdateDataBeginTransaction; property OnUpdateDataCommitTransaction : TDAUpdateDataTransactionEvent read fOnUpdateDataCommitTransaction write fOnUpdateDataCommitTransaction; property OnUpdateDataRollBackTransaction : TDAUpdateDataTransactionEvent read fOnUpdateDataRollBackTransaction write fOnUpdateDataRollBackTransaction; end; implementation uses Contnrs, Variants, uDARes, uDAExceptions, uDAXMLUtils, uROXMLIntf, TypInfo, uDAEngine, uDAWhere; { DataAbstractService } constructor TDataAbstractService.Create(aOwner : TComponent); begin fAllowDataAccess := true; fAllowSchemaAccess := true; fAllowDynamicSelect := true; fAllowDynamicWhere := true; fAllowUpdates := true; fAutoCreateBusinessProcessors := true; fProcessDeltasWithoutUpdateRules := true; fAcquireConnection := true; fExportedDataTables := TDADataTableReferenceCollection.Create(Self); inherited; end; destructor TDataAbstractService.Destroy; begin inherited; FreeAndNIL(fExportedDataTables); end; procedure TDataAbstractService.Notification(AComponent: TComponent; Operation: TOperation); var lRef: TDADataTableReference; begin inherited; if (Operation = opRemove) then begin if ((AComponent is TDADataTable) or (AComponent is TDataSet)) and (fExportedDataTables.Count > 0) then begin lRef := fExportedDataTables.FindByDataTable(TDADataTable(aComponent)); if (lRef<>NIL) then lRef.DataTable := NIL; end else if (AComponent = ServiceSchema) then ServiceSchema := nil else if (AComponent = ServiceDataStreamer) then ServiceDataStreamer := nil; end end; function TDataAbstractService.TriggerTransactionEvent(aEvent: TDAUpdateDataTransactionEvent): boolean; begin result := true; if assigned(aEvent) then aEvent(self, result); end; { IDataAbstractService: Schema Access } function TDataAbstractService.GetSchema(const aFilter: Utf8String): Utf8String; var xml: TStringStream; dummyrefs: TObjectList; tempSchema: TDASchema; procedure MergeDatatablesToSchema(aList: TObjectList); var i : integer; ref : TDADataset; dt : IDADataset; begin for i := 0 to fExportedDataTables.Count-1 do if fExportedDataTables[i].IsValidReference then begin dt := fExportedDataTables[i].Dataset; ref := tempSchema.Datasets.Add; ref.Name := dt.LogicalName; ref.Fields.AssignFieldCollection(dt.Fields); ref.Params.AssignParamCollection(dt.Params); aList.Add(ref); end; end; begin if not AllowSchemaAccess then raise Exception.Create('Schema access has been disabled (GetSchema)'); tempSchema := ServiceSchema; if not Assigned(tempSchema) and (fExportedDataTables.Count<>0) then tempSchema := TDASchema.Create(nil); if not Assigned(tempSchema) then raise Exception.Create('ServiceSchema property is not assigned and no data tables are exported.'); dummyrefs := nil; try // New: merges the data tables references by the service if (fExportedDataTables.Count>0) then begin dummyrefs := TObjectList.Create(true);; MergeDatatablesToSchema(dummyrefs); end; // Returns the schema xml := TStringStream.Create(''); try tempSchema.SaveToStream(xml); result := AnsiToUtf8(xml.DataString); if Assigned(fOnGetSchemaAsXML) then fOnGetSchemaAsXML(Self, result); finally xml.Free; end; finally FreeAndNil(dummyrefs); // automatically removes the datatables from the schema again if tempSchema <> ServiceSchema then tempSchema.Free; end; end; function TDataAbstractService.GetTableSchema(const aTableNameArray: StringArray): Utf8String; function CreateDatasetFromIDADataset(aRef: IDADataset): TDADataset; begin Result := TDADataset.Create(nil); Result.Name := aRef.LogicalName; Result.Fields.AssignFieldCollection(aRef.Fields); Result.Params.AssignParamCollection(aRef.Params); end; var lxml: IXMLDocument; i: integer; lDataSet: TDADataset; lDataTableRef: TDADataTableReference; lNeedDeleteDataset: Boolean; lTableName: string; begin if not AllowSchemaAccess then raise Exception.Create('Schema access has been disabled (GetTableSchema)'); if not Assigned(ServiceSchema) and (fExportedDataTables.Count=0) then raise Exception.Create('ServiceSchema property is not assigned and no data tables are exported.'); // Returns the schema lxml := NewROXmlDocument; lxml.New('DataTables'); try for i := 0 to aTableNameArray.Count - 1 do begin lTableName := Utf8ToAnsi(aTableNameArray[i]); lDataSet := nil; if (fExportedDataTables.Count > 0) then begin lDataTableRef := fExportedDataTables.FindByName(lTableName); if lDataTableRef <> nil then lDataSet := CreateDatasetFromIDADataset(lDataTableRef.Dataset); end; lNeedDeleteDataset := lDataSet <> nil; if not lNeedDeleteDataset and (ServiceSchema <> nil) then lDataSet := ServiceSchema.Datasets.DatasetByName(lTableName); try if lDataSet <> nil then begin // if Assigned(fBeforeGetDatasetSchema) then fBeforeGetDatasetSchema(Self, lDataset as IDADataset); SaveObjectToXMLNode(lDataSet, lxml.DocumentNode.Add('SchemaDataTable'), [], [], False); //if Assigned(FAfterGetDatasetSchema) then FAfterGetDatasetSchema(Self, lDataset as IDADataset); if not (lDataSet.IsPublic) then raise EDAException.Create(lDataset.Name +' is not accessible'); end; finally if lNeedDeleteDataset then lDataSet.Free; end; end; Result := UTF8Encode(lxml.DocumentNode.XML); finally lxml := nil; end; end; function TDataAbstractService.GetCommandSchema(const aCommandNameArray: StringArray): Utf8String; var lxml: IXMLDocument; i: integer; lSQLCommand: TDASQLCommand; begin if not AllowSchemaAccess then raise Exception.Create('Schema access has been disabled (GetCommandSchema)'); if not Assigned(ServiceSchema) then raise Exception.Create('ServiceSchema property is not assigned.'); // Returns the schema lxml := NewROXmlDocument; lxml.New('Commands'); try for i := 0 to aCommandNameArray.Count - 1 do begin lSQLCommand:=ServiceSchema.Commands.SQLCommandByName(Utf8ToAnsi(aCommandNameArray[i])); if lSQLCommand <> nil then begin if not lSQLCommand.IsPublic then raise EDAException.Create(lSqlCommand.Name +' is not accessible'); SaveObjectToXMLNode(lSQLCommand, lxml.DocumentNode.Add('SchemaCommand'), [], [], False); end; end; result := UTF8Encode(lxml.DocumentNode.XML); finally lxml := nil; end; end; { IDataAbstractService: Data Access } function TDataAbstractService.GetData(const aTableNameArray: StringArray; const aTableRequestInfoArray: TableRequestInfoArray): Binary; var i, j, x, n: integer; lParamNames: array of string; lParamValues: array of Variant; lOptions: TDAWriteOptions; lAllow: boolean; lDataSet: IDADataSet; lEditableDataset: IDAEditableDataset; lHasTransaction: boolean; lMaxRecords: integer; lDataTableRef: TDADataTableReference; lDynSelectFields: array of string; lDynSelectFields2: array of string; lWhereClause, lRemappedWhereClause: WideString; lConnection: IDAConnection; lTableName: String; lDataTable: TDADataset; lUnionTable: TDAUnionDataTable; lAppendData: TDADataForAppend; lMapping: TDAColumnMappingCollection; begin if not AllowDataAccess then raise Exception.Create('Data access has been disabled (GetData)'); result := nil; if not assigned(aTableNameArray) or (aTableNameArray.Count = 0) then exit; if assigned(aTableRequestInfoArray) and (aTableNameArray.Count <> aTableRequestInfoArray.Count) then raise Exception.Create('Number of items passed to aTableNameArray and aTableRequestInfoArray do not match.'); Check(True); result := Binary.Create; Result.CapacityIncrement := ServiceDataStreamer.BufferSize; try lHasTransaction := false; try ServiceDataStreamer.Initialize(result, aiWrite); try for i := 0 to (aTableNameArray.Count-1) do begin lTableName := Utf8ToAnsi(aTableNameArray[i]); lDataTable := ServiceSchema.FindDataset(lTableName); lOptions := [woRows]; lMaxRecords := -1; SetLength(lDynSelectFields,0); lWhereClause := ''; if assigned(aTableRequestInfoArray) and assigned(aTableRequestInfoArray[i]) then with aTableRequestInfoArray[i] do begin SetLength(lParamNames, Parameters.Count); SetLength(lParamValues, Parameters.Count); for j := 0 to (Parameters.Count-1) do begin lParamNames[j] := Utf8ToAnsi(Parameters[j].Name); lParamValues[j] := Parameters[j].Value; end; if IncludeSchema then lOptions := lOptions+[woSchema]; lMaxRecords := MaxRecords; // v5 TableRequestInfo if aTableRequestInfoArray[i] is TableRequestInfoV5 then with TableRequestInfoV5(aTableRequestInfoArray[i]) do begin if assigned(DynamicSelectFieldNames) then begin if AllowDynamicSelect then begin SetLength(lDynSelectFields,DynamicSelectFieldNames.Count); for j := 0 to DynamicSelectFieldNames.Count-1 do lDynSelectFields[j] := Trim(Utf8ToAnsi(DynamicSelectFieldNames[j])); if DynamicSelectFieldNames.Count > 0 then lOptions := lOptions+[woSchema]; end else begin raise EDAException.Create('DynamicSelect support has been disabled (GetData)'); end; end; if assigned(TableRequestInfoV5(aTableRequestInfoArray[i]).WhereClause) then begin if AllowDynamicWhere then lWhereClause := WhereClause.XML else raise EDAException.Create('DynamicWhere support has been disabled (GetData)'); end; end; end; if (fExportedDataTables.Count > 0) and Assigned(fExportedDataTables.FindByName(lTableName)) then begin lConnection := nil; end else begin Check(); //lConnection := GetConnectionForObject(aTableNameArray[i]); end; lAllow := true; if assigned(fGetDatasetDataValidation) then fGetDatasetDataValidation(self, {l}Connection, lTableName, lParamNames, lParamValues, ServiceSchema, lAllow); if not lAllow then raise EDADatasetNotAccessible.CreateFmt(err_DatasetNotAccessible, [lTableName]); lDataSet := nil; if (fExportedDataTables.Count > 0) then begin lDataTableRef := fExportedDataTables.FindByName(lTableName); if assigned(lDataTableRef) then lDataSet := lDataTableRef.Dataset; end; if not assigned(lDataSet) then begin // Create transaction, if needed if not lHasTransaction then begin if TriggerTransactionEvent(fOnUpdateDataBeginTransaction) then begin Connection.BeginTransaction; lHasTransaction := true; end; end; lDataTable := ServiceSchema.FindDataset(lTableName); { Unions } if lDataTable is TDAUnionDataTable then begin if (aTableRequestInfoArray <> nil) and (Trim(Utf8ToAnsi(aTableRequestInfoArray[i].UserFilter))<>'') then raise EDAException.Create('Passing of clear text WHERE clauses is not supported for UNIONS (GetData)'); lUnionTable := lDataTable as TDAUnionDataTable; lAppendData := ServiceDataStreamer.BeginWriteDataset({Source}nil, {Schema}lDataTable, lOptions, lMaxRecords, lDynSelectFields); if not (woRows in lOptions) then Continue; for x := 0 to lUnionTable.SourceTables.Count - 1 do begin lTableName := lUnionTable.SourceTables[x].Name; // Check is source table exists in schema. if (not Assigned(ServiceSchema.FindDataset(lTableName))) then raise EDAException.CreateFmt('Source table %s doesn''t exist in schema.', [lTableName]); // Do column remapping for DynFields names lMapping := lUnionTable.SourceTables[x].ColumnMappings; SetLength(lDynSelectFields2, Length(lDynSelectFields)); for n := Low(lDynSelectFields) to High(lDynSelectFields) do begin if (lDynSelectFields[n]) = def_SourceTableFieldName then lDynSelectFields2[n] := lDynSelectFields[n] else lDynSelectFields2[n] := lMapping.MappingByDatasetField(lDynSelectFields[n]).TableField; end; lRemappedWhereClause := Where_RemapFieldNames(lWhereClause, lMapping); lDataSet := ServiceSchema.NewDataset(GetConnectionForObject(lTableName), lTableName, lParamNames, lParamValues, lDynSelectFields2, lRemappedWhereClause, false); ServiceDataStreamer.WriteDatasetData(lDataSet, lAppendData, x); // Keeping track of maxrecords if ((lMaxRecords <> -1) and (lAppendData.RecordCount >= lMaxRecords)) then Break; end; ServiceDataStreamer.EndWriteDataset(lAppendData); end { Joins } else if lDataTable is TDAJoinDataTable then begin raise EDAException.Create('TODO: Joined Data Tables are not implemented in this release, yet.'); end { Plain Data Tables } else begin lDataSet := ServiceSchema.NewDataset(GetConnectionForObject(lTableName), lTableName, lParamNames, lParamValues, lDynSelectFields, lWhereClause, False); if (woSchema in lOptions) and Assigned(fBeforeGetDatasetSchema) then fBeforeGetDatasetSchema(self, lDataset); if (woRows in lOptions) and Assigned(fBeforeGetDatasetData) then fBeforeGetDatasetData(self, lDataset, (woSchema in lOptions), lMaxRecords); if (aTableRequestInfoArray <> nil) and (Trim(Utf8ToAnsi(aTableRequestInfoArray[i].UserFilter))<>'') then begin if not AllowWhereSQL then raise Exception.Create('Passing of clear text WHERE clauses has been disabled (GetData)'); lDataSet.Where.AddText(Utf8ToAnsi(aTableRequestInfoArray[i].UserFilter)); end; lDataSet.Open; ServiceDataStreamer.WriteDataset(lDataset, lOptions, lMaxRecords,lDynSelectFields); if (woRows in lOptions) and Assigned(fAfterGetDatasetData) then fAfterGetDatasetData(self, lDataset, (woSchema in lOptions), lMaxRecords); if (woSchema in lOptions) and Assigned(fAfterGetDatasetSchema) then fAfterGetDatasetSchema(self, lDataset); end; end { ExportedDataTables } else begin if (Trim(Utf8ToAnsi(aTableRequestInfoArray[i].UserFilter))<>'') then raise EDAException.Create('WHERE clauses are not supported on data from ExportedDataTables.'); if (woSchema in lOptions) and Assigned(fBeforeGetDatasetSchema) then fBeforeGetDatasetSchema(self, lDataset); if (woRows in lOptions) and Assigned(fBeforeGetDatasetData) then fBeforeGetDatasetData(self, lDataset, (woSchema in lOptions), lMaxRecords); lDataSet.Open; ServiceDataStreamer.WriteDataset(lDataset, lOptions, lMaxRecords,lDynSelectFields); if (woRows in lOptions) and Assigned(fAfterGetDatasetData) then fAfterGetDatasetData(self, lDataset, (woSchema in lOptions), lMaxRecords); if (woSchema in lOptions) and Assigned(fAfterGetDatasetSchema) then fAfterGetDatasetSchema(self, lDataset); end; if assigned(lDataTable) and not (lDataTable.IsPublic) then raise EDAException.Create(lDataTable.Name +' is not accessible'); if lMaxRecords = -1 then begin if lDataSet.QueryInterface(IDAEditableDataset,lEditableDataset) = s_ok then lEditableDataset.First else lDataSet.Close; end; lDataSet := nil; end; { for } finally ServiceDataStreamer.Finalize(); end; if lHasTransaction and Connection.InTransaction and TriggerTransactionEvent(fOnUpdateDataCommitTransaction) then Connection.CommitTransaction; except if lHasTransaction and Connection.InTransaction and TriggerTransactionEvent(fOnUpdateDataRollBackTransaction) then Connection.RollbackTransaction; raise; end; except FreeAndNIL(result); raise; end; end; //TODO: needs cleaning! function TDataAbstractService.UnpackDeltas(const DeltaStream: Binary; DeltaStructList: TDADeltaStructList): TDADeltasModes; var i, j: integer; lDeltaName: string; lBizProc: TDABusinessProcessor; lDetails : TDADatasetRelationshipList; lFound: boolean; lStruct : TDADeltaStruct; ltabRef: TDADataTableReference; begin result := []; Check(True); // Reads the deltas. ServiceDataStreamer.Initialize(DeltaStream, aiReadFromBeginning); try if (ServiceDataStreamer.DeltaCount = 0) then Exit; for i := 0 to (ServiceDataStreamer.DeltaCount - 1) do begin lDeltaName := ServiceDataStreamer.DeltaNames[i]; if (ExportedDataTables.Count >0) then begin ltabRef := ExportedDataTables.FindByName(lDeltaName); if Assigned(ltabRef) then begin // for ExportedDataTables , BP=nil lStruct := DeltaStructList.Add(NewDelta(lDeltaName), nil); ServiceDataStreamer.ReadDelta(lDeltaName, lStruct.Delta); Result:=Result+[dumExported]; if ServiceDataStreamer.HasReducedDelta then begin raise Exception.Create('Reduced Delta is not supported for ExportedDataTables'); end; Continue; end; end; lFound := false; { Tries to locate a user-defined business processor } for j := 0 to (Self.ComponentCount - 1) do begin if (Self.Components[j] is TDABusinessProcessor) then begin lBizProc := TDABusinessProcessor(Self.Components[j]); if SameText(lBizProc.ReferencedDataset, lDeltaName) then begin lStruct := DeltaStructList.Add(NewDelta(lDeltaName), lBizProc); ServiceDataStreamer.ReadDelta(lDeltaName, lStruct.Delta); if ServiceDataStreamer.HasReducedDelta then begin if (lBizProc.InsertCommandName <> '') or (lBizProc.DeleteCommandName <> '') or (lBizProc.UpdateCommandName <> '') or ([poAutoGenerateInsert,poAutoGenerateUpdate,poAutoGenerateDelete]*lBizProc.ProcessorOptions <>[poAutoGenerateInsert,poAutoGenerateUpdate,poAutoGenerateDelete]) then raise Exception.Create('SendReducedDelta option is uncompatible with '+lBizProc.Name+' settings'); end; lFound := true; Break; end; end; end; { Either creates one or aborts raising an exception } if not lFound then begin if not AutoCreateBusinessProcessors then raise Exception.CreateFmt(err_DARDMCannotFindProxessorForDelta, [lDeltaName]); //ToDo: this will never get freed, until the DM frees? lBizProc := TDABusinessProcessor.Create(self); lBizProc.ReferencedDataset := lDeltaName; lBizProc.Schema := ServiceSchema; if Assigned(fOnBusinessProcessorAutoCreated) then fOnBusinessProcessorAutoCreated(Self, lBizProc); lStruct := DeltaStructList.Add(NewDelta(lDeltaName), lBizProc); ServiceDataStreamer.ReadDelta(lDeltaName, lStruct.Delta); end; Result:=Result+[dumStandard]; end; fHasReducedDelta := ServiceDataStreamer.HasReducedDelta; if not (dumStandard in Result) then exit; // nothing to process Check; { Sets the master/detail relationships } if (ServiceSchema.RelationShips.Count>0) then begin lDetails := TDADatasetRelationshipList.Create; try for i := 0 to DeltaStructList.Count-1 do begin ServiceSchema.RelationShips.GetDetails(DeltaStructList[i].BusinessProcessor.ReferencedDataset, lDetails); if (lDetails.Count=0) then Continue; { Prepares an array with the references to the detail deltas that will be used later on to adjust autoincs, etc. } for j := 0 to lDetails.Count-1 do begin lStruct := DeltaStructList.FindStruct(lDetails[j].DetailDatasetName); if assigned(lStruct) then begin DeltaStructList[i].DetailDeltas.Add(lStruct.Delta); DeltaStructList[i].RelationShips.Add(lDetails[j]); end; end; end; finally lDetails.Free; end; end; finally ServiceDataStreamer.Finalize; end; end; function TDataAbstractService.UpdateData(const aDelta: Binary): Binary; var lProcessedDeltas: TStringList; lDeltaStructs: TDADeltaStructList; lStruct: TDADeltaStruct; lDs: TDADataset; lDoRaise: boolean; i,j: integer; modes: TDADeltasModes; oldDeltaMode: Boolean; begin if not AllowDataAccess then raise Exception.Create('Data access has been disabled (UpdateData)'); if not AllowUpdates then raise Exception.Create('Data updates have been disabled (UpdateData)'); result := nil; fHasReducedDelta := False; lDeltaStructs := TDADeltaStructList.Create; try try modes:= UnpackDeltas(aDelta, lDeltaStructs); if modes = [] then exit; if dumStandard in modes then begin Check; if TriggerTransactionEvent(fOnUpdateDataBeginTransaction) then Connection.BeginTransaction; end; if Assigned(fBeforeProcessDeltas) then fBeforeProcessDeltas(Self, lDeltaStructs); for i := 0 to lDeltaStructs.Count-1 do begin lDs := ServiceSchema.FindDataset(lDeltaStructs[i].Delta.LogicalName); if lDs <> nil then begin if not lDs.IsPublic then raise EDAException.Create(lDs.Name+' is not accessible'); if lDs.ReadOnly then raise EDAException.Create(lDs.Name+' is read-only'); end; end; if dumExported in modes then // Processes the delta for exported tables (BP=nil) for i := 0 to lDeltaStructs.Count-1 do begin if lDeltaStructs[i].BusinessProcessor = nil then ExportedDataTables_MergeDelta(lDeltaStructs[i].Delta); end; if dumStandard in modes then begin if (ServiceSchema.UpdateRules.Count = 0) and ProcessDeltasWithoutUpdateRules then begin // Processes them in order, from first to last delta sent for i := 0 to lDeltaStructs.Count-1 do if lDeltaStructs[i].BusinessProcessor <> nil then begin oldDeltaMode:=lDeltaStructs[i].BusinessProcessor.HasReducedDelta; try lDeltaStructs[i].BusinessProcessor.HasReducedDelta:=fHasReducedDelta; // TODO: Handle GetConnectionForObject() for 'Rosetta', UNIONS and JOINS lDeltaStructs[i].BusinessProcessor.ProcessDelta(GetConnectionForObject(lDeltaStructs[i].BusinessProcessor.ReferencedDataset), lDeltaStructs[i].Delta, AllChanges); finally lDeltaStructs[i].BusinessProcessor.HasReducedDelta:=oldDeltaMode; end; with lDeltaStructs[i] do for j := 0 to (DetailDeltas.Count-1) do BusinessProcessor.SynchronizeAutoIncs(Delta, DetailDeltas[j], RelationShips[j]); end; end else begin lProcessedDeltas := TStringList.Create; try for i := 0 to (ServiceSchema.UpdateRules.Count-1) do begin // Processes them in the order defined in the schema lStruct := lDeltaStructs.FindStruct(ServiceSchema.UpdateRules[i].DatasetName); if assigned(lStruct) then begin if lStruct.BusinessProcessor = nil then Continue; // Adds the dataset name to the list of processed deltas. Those that don't have update rules will be processed later lProcessedDeltas.Add(lStruct.Delta.LogicalName); // Processes the delta oldDeltaMode:=lStruct.BusinessProcessor.HasReducedDelta; try lStruct.BusinessProcessor.HasReducedDelta:=fHasReducedDelta; // TODO: Handle GetConnectionForObject() for 'Rosetta', UNIONS and JOINS lStruct.BusinessProcessor.ProcessDelta(GetConnectionForObject(lStruct.BusinessProcessor.ReferencedDataset), lStruct.Delta, ServiceSchema.UpdateRules[i].ChangeTypes); finally lStruct.BusinessProcessor.HasReducedDelta:=oldDeltaMode; end; if (ctInsert in ServiceSchema.UpdateRules[i].ChangeTypes) then begin for j := 0 to (lStruct.DetailDeltas.Count-1) do lStruct.BusinessProcessor.SynchronizeAutoIncs(lStruct.Delta, lStruct.DetailDeltas[j], lStruct.RelationShips[j]); end; end; end; // Processes the deltas for which update rules were not defined if (ProcessDeltasWithoutUpdateRules) then begin for i := 0 to lDeltaStructs.Count-1 do begin // Skips if already processed if (lProcessedDeltas.IndexOf(lDeltaStructs[i].Delta.LogicalName)>=0) then Continue; if lDeltaStructs[i].BusinessProcessor <> nil then begin oldDeltaMode:=lDeltaStructs[i].BusinessProcessor.HasReducedDelta; try lDeltaStructs[i].BusinessProcessor.HasReducedDelta:=fHasReducedDelta; lDeltaStructs[i].BusinessProcessor.ProcessDelta(GetConnectionForObject(lDeltaStructs[i].BusinessProcessor.ReferencedDataset), lDeltaStructs[i].Delta, AllChanges); finally lDeltaStructs[i].BusinessProcessor.HasReducedDelta:=oldDeltaMode; end; with lDeltaStructs[i] do for j := 0 to (DetailDeltas.Count-1) do BusinessProcessor.SynchronizeAutoIncs(Delta, DetailDeltas[j], RelationShips[j]); end; end; end; finally lProcessedDeltas.Free; end; end; end; if Assigned(fAfterProcessDeltas) then fAfterProcessDeltas(Self, lDeltaStructs); if dumStandard in modes then if Connection.InTransaction and TriggerTransactionEvent(fOnUpdateDataCommitTransaction) then Connection.CommitTransaction; result := Binary.Create; oldDeltaMode:=ServiceDataStreamer.SendReducedDelta; ServiceDataStreamer.Initialize(result, aiWrite); try if fHasReducedDelta then ServiceDataStreamer.SendReducedDelta:=True; for i := 0 to lDeltaStructs.Count-1 do ServiceDataStreamer.WriteDelta(lDeltaStructs[i].Delta); finally ServiceDataStreamer.Finalize; ServiceDataStreamer.SendReducedDelta:=oldDeltaMode; end; except on E:Exception do begin lDoRaise := true; try if Assigned(fOnProcessDeltasError) then fOnProcessDeltasError(Self, lDeltaStructs, E, lDoRaise); finally if dumStandard in modes then if Connection.InTransaction and TriggerTransactionEvent(fOnUpdateDataRollBackTransaction) then Connection.RollbackTransaction; end; if lDoRaise then raise; end; end; finally lDeltaStructs.Free; end; end; function TDataAbstractService.ExecuteCommand(const aCommandName: Utf8String; const aParameterArray: DataParameterArray): Integer; var j: integer; lParamNames: array of string; lParamValues: array of Variant; lAllow: boolean; lCommand: IDASQLCommand; lCommandSchema: TDASQLCommand; lConnection: IDAConnection; lCommandName: string; begin if not AllowDataAccess then raise Exception.Create('Data access has been disabled (ExecuteCommand)'); if not AllowExecuteCommands then raise Exception.Create('Execution of commands has been disabled (ExecuteCommand)'); SetLength(lParamNames, aParameterArray.Count); SetLength(lParamValues, aParameterArray.Count); for j := 0 to (aParameterArray.Count-1) do begin lParamNames[j] := Utf8ToAnsi(aParameterArray[j].Name); lParamValues[j] := aParameterArray[j].Value; end; lCommandName:=Utf8ToAnsi(aCommandName); lCommandSchema := ServiceSchema.Commands.SQLCommandByName(lCommandName); if assigned(lCommandSchema) and not (lCommandSchema.IsPublic) then raise EDAException.Create(lCommandSchema.Name +' is not accessible'); lAllow := true; lConnection := GetConnectionForObject(lCommandName); if assigned(fExecuteCommandValidation) then fExecuteCommandValidation(self, lConnection, lCommandName, lParamNames, lParamValues, ServiceSchema, lAllow); if not lAllow then raise EDADatasetNotAccessible.CreateFmt(err_CommandNotAccessible, [lCommandName]); if TriggerTransactionEvent(fOnUpdateDataBeginTransaction) then lConnection.BeginTransaction; try lCommand := ServiceSchema.NewCommand(lConnection, lCommandName, lParamNames, lParamValues, false); if Assigned(fBeforeExecutecommand) then fBeforeExecuteCommand(self, lCommand); result := lCommand.Execute; if assigned(fAfterExecutecommand) then fAfterExecuteCommand(self, lCommand, Result); if lConnection.InTransaction and TriggerTransactionEvent(fOnUpdateDataCommitTransaction) then lConnection.CommitTransaction; except if lConnection.InTransaction and TriggerTransactionEvent(fOnUpdateDataRollBackTransaction) then lConnection.RollbackTransaction; raise; end; end; { IDataAbstractService: Direct SQL Access } function TDataAbstractService.SQLGetData(const aSQLText: Utf8String; const aIncludeSchema: Boolean; const aMaxRecords: Integer): Binary; var lOptions: TDAWriteOptions; lDataSet: IDADataSet; lAllow: boolean; lSQLText: string; begin if not AllowDataAccess then raise Exception.Create('Data access has been disabled (SQLGetData)'); if not AllowExecuteSQL then raise Exception.Create('Execution of SQL has been disabled (SQLGetData)'); if assigned(fHETConnection) then raise Exception.Create('Execution of SQL isnot supported for HET Connections'); result := nil; lSQLText := Utf8ToAnsi(aSQLText); if lSQLText = '' then exit; Check(); lAllow := true; if assigned(fSQLValidation) then fSQLValidation(self, Connection, lSQLText, [], [], lAllow); if not lAllow then raise EDADatasetNotAccessible.Create(err_SQLNotPermitted); result := Binary.Create; Result.CapacityIncrement := ServiceDataStreamer.BufferSize; try if TriggerTransactionEvent(fOnUpdateDataBeginTransaction) then Connection.BeginTransaction; try ServiceDataStreamer.Initialize(result, aiWrite); try lOptions := [woRows]; if aIncludeSchema then lOptions := lOptions+[woSchema]; lDataSet := Connection.NewDataset(lSQLText,'SQLResult'); try if (woSchema in lOptions) and Assigned(fBeforeGetDatasetSchema) then fBeforeGetDatasetSchema(self, lDataset); if (woRows in lOptions) and Assigned(fBeforeGetDatasetData) then fBeforeGetDatasetData(self, lDataset, (woSchema in lOptions), aMaxRecords); lDataSet.Open(); ServiceDataStreamer.WriteDataset(lDataset, lOptions, aMaxRecords); if (woRows in lOptions) and Assigned(fAfterGetDatasetData) then fAfterGetDatasetData(self, lDataset, (woSchema in lOptions), aMaxRecords); if (woSchema in lOptions) and Assigned(fAfterGetDatasetSchema) then fAfterGetDatasetSchema(self, lDataset); finally lDataSet := nil; end; finally ServiceDataStreamer.Finalize(); end; if Connection.InTransaction and TriggerTransactionEvent(fOnUpdateDataCommitTransaction) then Connection.CommitTransaction; except if Connection.InTransaction and TriggerTransactionEvent(fOnUpdateDataRollBackTransaction) then Connection.RollbackTransaction; raise; end; except FreeAndNIL(result); raise; end; end; function TDataAbstractService.SQLExecuteCommand(const aSQLText: Utf8String): Integer; var lAllow: boolean; lSQLText: string; begin if not AllowDataAccess then raise Exception.Create('Data access has been disabled (SQLExecuteCommand)'); if not AllowExecuteSQL then raise Exception.Create('Execution of SQL has been disabled (SQLExecuteCommand)'); if assigned(fHETConnection) then raise Exception.Create('Execution of SQL isnot supported for HET Connections'); Check(); lSQLText := Utf8ToAnsi(aSQLText); lAllow := true; if assigned(fSQLValidation) then fSQLValidation(self, Connection, lSQLText, [], [], lAllow); if not lAllow then raise EDADatasetNotAccessible.CreateFmt(err_SQLNotPermitted, []); if TriggerTransactionEvent(fOnUpdateDataBeginTransaction) then Connection.BeginTransaction; try result := Connection.NewCommand(lSQLText, stSQL).Execute; if Connection.InTransaction and TriggerTransactionEvent(fOnUpdateDataCommitTransaction) then Connection.CommitTransaction; except if Connection.InTransaction and TriggerTransactionEvent(fOnUpdateDataRollBackTransaction) then Connection.RollbackTransaction; raise; end; end; { Properties } procedure TDataAbstractService.SetExportedDataTables(const Value: TDADataTableReferenceCollection); begin fExportedDataTables.Assign(Value); end; procedure TDataAbstractService.SetServiceDataStreamer(const Value: TDADataStreamer); begin fServiceDataStreamer := Value; if (fServiceDataStreamer <> nil) then fServiceDataStreamer.FreeNotification(Self); end; procedure TDataAbstractService.SetServiceSchema(const Value: TDASchema); begin fServiceSchema := Value; if assigned(fServiceSchema) then fServiceSchema.FreeNotification(self); end; { Connections } function TDataAbstractService.GetConnection: IDAConnection; var lConnectionName: string; begin inherited; result := fConnection; if assigned(fConnection) then exit; if (csDesigning in ComponentState) then exit; if AcquireConnection then try if (fServiceSchema = nil) or (fServiceSchema.ConnectionManager = nil) then raise Exception.Create(err_DARDMInvalidSchema); lConnectionName := ConnectionName; if Assigned(fBeforeAcquireConnection) then fBeforeAcquireConnection(Self, lConnectionName); result := ServiceSchema.ConnectionManager.NewConnection(lConnectionName); Connection := result; if Assigned(fAfterAcquireConnection) then fAfterAcquireConnection(Self, lConnectionName, result); except on E: Exception do begin if Assigned(fOnAcquireConnectionFailure) then fOnAcquireConnectionFailure(Self, lConnectionName, E); raise; end; end; end; {$WARN SYMBOL_DEPRECATED OFF} procedure TDataAbstractService.SetConnection(aConnection: IDAConnection); begin if fConnection <> aConnection then begin fConnection := aConnection; if not Supports(fConnection, IDAHETConnection, fHETConnection) then fHETConnection := nil; end; end; {$WARN SYMBOL_DEPRECATED ON} function TDataAbstractService.GetConnectionForObject(const aName: string): IDAConnection; begin result := Connection; // forces acquisition of Connection if assigned(fHETConnection) then result := fHETConnection.GetConnectionForObject(aName) end; procedure TDataAbstractService.DoOnDeactivate(aClientID: TGUID); begin inherited; if (csDesigning in ComponentState) then Exit; ReleaseConnection(); end; procedure TDataAbstractService.ReleaseConnection; var lConnectionName: string; begin if assigned(fConnection) then begin lConnectionName := fConnection.Name; if Assigned(fBeforeReleaseConnection) then fBeforeReleaseConnection(Self, lConnectionName, fConnection); Connection := nil; if Assigned(fAfterReleaseConnection) then fAfterReleaseConnection(Self, lConnectionName); end; end; procedure TDataAbstractService.Check(CheckOnlyDataStreamer:Boolean = false); begin if not CheckOnlyDataStreamer then begin if not (Assigned(Connection) or AcquireConnection) then raise EROException.Create('AcquireConnection is not set and there is no connection available'); if not Assigned(ServiceSchema) then raise Exception.Create('ServiceSchema property is not assigned.'); end; if not Assigned(ServiceDataStreamer) then raise EROException.Create('DataStreamer must be assigned.'); end; function TDataAbstractService.ExecuteCommandEx(const aCommandName: Utf8String; const aInputParameters: DataParameterArray; out aOutputParameters: DataParameterArray): Integer; var j: integer; lParamNames: array of string; lParamValues: array of Variant; lAllow: boolean; lCommand: IDASQLCommand; lCommandSchema: TDASQLCommand; lConnection: IDAConnection; lCommandName: string; begin if not AllowDataAccess then raise Exception.Create('Data access has been disabled (ExecuteCommand)'); if not AllowExecuteCommands then raise Exception.Create('Execution of commands has been disabled (ExecuteCommand)'); lCommandName:= Utf8ToAnsi(aCommandName); SetLength(lParamNames, aInputParameters.Count); SetLength(lParamValues, aInputParameters.Count); for j := 0 to (aInputParameters.Count-1) do begin lParamNames[j] := Utf8ToAnsi(aInputParameters[j].Name); lParamValues[j] := aInputParameters[j].Value; end; lCommandSchema := ServiceSchema.Commands.SQLCommandByName(lCommandName); if assigned(lCommandSchema) and not (lCommandSchema.IsPublic) then raise EDAException.Create(lCommandSchema.Name +' is not accessible'); lAllow := true; lConnection := GetConnectionForObject(lCommandName); if assigned(fExecuteCommandValidation) then fExecuteCommandValidation(self, lConnection, lCommandName, lParamNames, lParamValues, ServiceSchema, lAllow); if not lAllow then raise EDADatasetNotAccessible.CreateFmt(err_CommandNotAccessible, [lCommandName]); if TriggerTransactionEvent(fOnUpdateDataBeginTransaction) then lConnection.BeginTransaction; try lCommand := ServiceSchema.NewCommand(lConnection, lCommandName, lParamNames, lParamValues, false); if Assigned(fBeforeExecutecommand) then fBeforeExecuteCommand(self, lCommand); result := lCommand.Execute; if assigned(fAfterExecutecommand) then fAfterExecuteCommand(self, lCommand, Result); aOutputParameters := DataParameterArray.Create; for j := 0 to lCommand.Params.Count -1 do begin if (lCommand.Params[j].ParamType <> daptUnknown) and (lCommand.Params[j].ParamType <> daptInput) then begin with aOutputParameters.Add do begin Name := AnsiToUtf8(lCommand.Params[j].Name); Value := lCommand.Params[j].Value; end; end; end; if lConnection.InTransaction and TriggerTransactionEvent(fOnUpdateDataCommitTransaction) then lConnection.CommitTransaction; except if lConnection.InTransaction and TriggerTransactionEvent(fOnUpdateDataRollBackTransaction) then lConnection.RollbackTransaction; raise; end; end; function TDataAbstractService.GetDatasetScripts(const DatasetNames: Utf8String): Utf8String; var names : TStringList; i : integer; ds : TDADataset; lResult: WideString; begin lresult := ''; names := TStringList.Create; try names.CommaText :=Utf8ToAnsi(DatasetNames); for i := 0 to (names.Count-1) do begin // scripts for externaltables is not allowed if fExportedDataTables.FindByName(names[i]) <> nil then continue; ds := ServiceSchema.Datasets.DatasetByName(names[i]); lresult := lresult+Format('<%s Language="%s">', [ names[i], GetEnumName(TypeInfo(TROSEScriptLanguage), Ord(ds.BusinessRulesClient.ScriptLanguage)), ds.BusinessRulesClient.Script, names[i]]); end; result := UTF8Encode(''+lresult+''); finally FreeAndNIL(names); end; end; procedure TDataAbstractService.ExportedDataTables_MergeDelta(ADelta: IDADelta); var i, k, x: integer; oldval, newval, val : Variant; fld : TDAField; pkfields : string; pkfields1: array of string; insertfields: array of string; keyvals, insertvals : array of variant; LDAEditableDataset:IDAEditableDataset; ADataset: IDADataset; pk_array: array of boolean; begin // validated in UnpackDeltas ADataset:=ExportedDataTables.FindByName(ADelta.LogicalName).Dataset; SetLength(pk_array, ADelta.LoggedFieldCount); for i := 0 to ADelta.LoggedFieldCount - 1 do pk_array[i]:=False; for i := 0 to ADelta.KeyFieldCount - 1 do begin x := ADelta.IndexOfLoggedField(ADelta.KeyFieldNames[i]); if x <> -1 then pk_array[x]:=True; end; SetLength(pkfields1, ADelta.KeyFieldCount); for i := 0 to (ADelta.KeyFieldCount-1) do pkfields1[i]:=ADelta.KeyFieldNames[i]; if ADelta.KeyFieldCount = 0 then begin SetLength(pkfields1, ADelta.LoggedFieldCount); k:=-1; for i := 0 to (ADelta.LoggedFieldCount-1) do begin if ADelta.LoggedFieldTypes[i] in [datUnknown, datMemo, datBlob,datWideMemo] then Continue; inc(k); pkfields1[k]:=ADelta.LoggedFieldNames[i]; end; SetLength(pkfields1,k+1); end; SetLength(keyvals, Length(pkfields1)); pkfields := ''; For i:=0 to Length(pkfields1) - 1 do pkfields:=pkfields+pkfields1[i]+';'; pkfields := Copy(pkfields, 1, Length(pkfields)-1); if (ADelta.Count>0) then begin for i := (ADelta.Count-1) downto 0 do begin for k := 0 to Length(pkfields1)-1 do begin val := ADelta[i].OldValueByName[pkfields1[k]]; keyvals[k] := val; end; if fHasReducedDelta and (ADelta.KeyFieldCount = 0) then begin SetLength(pkfields1, ADelta.LoggedFieldCount); k:=-1; for x := 0 to (ADelta.LoggedFieldCount-1) do begin if ADelta.LoggedFieldTypes[x] in [datUnknown, datMemo, datBlob,datWideMemo] then Continue; pkfields:=ADelta.LoggedFieldNames[x]; if ROVariantsEqual(ADelta[i].OldValueByName[pkfields],ADelta[i].NewValueByName[pkfields]) then Continue; inc(k); pkfields1[k]:=ADelta.LoggedFieldNames[x]; end; SetLength(pkfields1,k+1); for k := 0 to Length(pkfields1)-1 do begin val := ADelta[i].OldValueByName[pkfields1[k]]; keyvals[k] := val; end; pkfields := ''; For x:=0 to Length(pkfields1) - 1 do pkfields:=pkfields+pkfields1[x]+';'; pkfields := Copy(pkfields, 1, Length(pkfields)-1); end; if ADataset.QueryInterface(StringToGUID('{D3E2147F-65B3-4D9D-8614-7270011FA7D5}'),LDAEditableDataset) <> 0 then exit; LDAEditableDataset.First; case ADelta[i].ChangeType of ctDelete: begin // Locates the original record if not Adataset.Locate(pkfields, keyvals, []) then Continue; LDAEditableDataset.Delete; end; ctInsert: begin // fHasReducedDelta don't work in this case // old values = Unassigned // new Values = Null SetLength(insertFields, ADelta.LoggedFieldCount); SetLength(insertvals, ADelta.LoggedFieldCount); for x := 0 to (ADelta.LoggedFieldCount-1) do begin insertfields[x] := ADelta.LoggedFieldNames[x]; insertvals[x] := ADelta[i].NewValueByName[ADelta.LoggedFieldNames[x]]; end; LDAEditableDataset.AddRecord(insertfields,insertvals); end; ctUpdate: begin // Locates the original record if not Adataset.Locate(pkfields, keyvals, []) then Continue; LDAEditableDataset.Edit; for x := 0 to (ADelta.LoggedFieldCount-1) do begin fld := Adataset.FieldByName(ADelta.LoggedFieldNames[x]); newval := ADelta[i].NewValueByName[fld.Name]; oldval := ADelta[i].OldValueByName[fld.Name]; if fHasReducedDelta and (ROVariantsEqual(newval, oldval)) then Continue; fld.Value:=newval; { if fld.ServerAutoRefresh or (not VarIsArray(newVal) and (newval<>oldval)) then begin VariantToFieldValue(newval, fld); end; } end; LDAEditableDataset.Post; end; end; // Removes this merged change ADelta.Delete(i); end; end end; procedure TDataAbstractService.RegisterForDataChangeNotification( const aTableName: Utf8String); begin { TODO : todo } end; procedure TDataAbstractService.UnregisterForDataChangeNotification( const aTableName: Utf8String); begin { TODO : todo } end; end.