1364 lines
58 KiB
ObjectPascal
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.
|