Componentes.Terceros.RemObj.../official/5.0.24.615/Data Abstract for Delphi/Source/DataAbstractService_Impl.pas

1364 lines
58 KiB
ObjectPascal

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]);
if ServiceSchema <> nil then lDataTable := ServiceSchema.FindDataset(lTableName) else lDataTable:=nil;
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];
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
if ServiceSchema <> nil then
lDs := ServiceSchema.FindDataset(lDeltaStructs[i].Delta.LogicalName)
else
lDs := nil;
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"><![CDATA[%s]]></%s>', [
names[i],
GetEnumName(TypeInfo(TROSEScriptLanguage), Ord(ds.BusinessRulesClient.ScriptLanguage)),
ds.BusinessRulesClient.Script,
names[i]]);
end;
result := UTF8Encode('<Scripts>'+lresult+'</Scripts>');
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];
oldval:=ADelta[i].OldValueByName[pkfields];
if ROVariantsEqual(oldval,ADelta[i].NewValueByName[pkfields]) and (VarIsNull(oldval) or (VarIsEmpty(oldval))) then Continue;
inc(k);
pkfields1[k]:=ADelta.LoggedFieldNames[x];
end;
SetLength(pkfields1,k+1);
SetLength(keyvals,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.Open;
LDAEditableDataset.First;
case ADelta[i].ChangeType of
ctDelete: begin
// Locates the original record
if Length(keyvals)=1 then begin
if not Adataset.Locate(pkfields, keyvals[0], []) then Continue;
end
else begin
if not Adataset.Locate(pkfields, keyvals, []) then Continue;
end;
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 Length(keyvals)=1 then begin
if not Adataset.Locate(pkfields, keyvals[0], []) then Continue;
end
else begin
if not Adataset.Locate(pkfields, keyvals, []) then Continue;
end;
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.