unit uDAHETDriver; {----------------------------------------------------------------------------} { Data Abstract Library - 'Rosetta' Library - HIGHLY CONFIDENTIAL { { compiler: Delphi 6 and up { platform: Win32 { { (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. {----------------------------------------------------------------------------} {$I DataAbstract.inc} interface // {$R DataAbstract_Rosetta_Glyphs.res} uses Classes, SysUtils, uDAInterfaces, uDAClasses, uDAEngine, uDARes, uDADriverManager, uDAHETConnections, DB; type TDAHETDriver = class(TDADriverReference) end; TDAEHETDriver = class(TDAEDriver) protected function GetConnectionClass: TDAEConnectionClass; override; function GetDriverID: string; override; function GetDescription: string; override; public end; TDAEHETConnection = class(TDAEConnection, IDAHETConnection) private fInTransaction: boolean; fActiveConnections: TStringList; function GetConnectionByName(const aConnectionName: string; aOpenConnection: boolean = false): IDAConnection; protected function DoBeginTransaction: Integer; override; procedure DoCommitTransaction; override; procedure DoRollbackTransaction; override; function DoGetInTransaction: Boolean; override; function CreateCustomConnection: TCustomConnection; override; function GetConnected: Boolean; override; {$IFNDEF FPC_SAFECALL_BUG}safecall;{$ENDIF} public constructor Create(aDriver: TDAEDriver; aName: string = ''); override; destructor Destroy; override; { IDAHETConnection } function GetConnectionForObject(const aObjectName: string; aOpenConnection: boolean = false): IDAConnection; end; TDAHETConnectionContainer = class private fConnection: IDAConnection; constructor Create(aConnection: IDAConnection); end; procedure Register; function GetDriverObject: IDADriver; stdcall; implementation { TDAEHETDriver } function TDAEHETDriver.GetConnectionClass: TDAEConnectionClass; begin result := TDAEHETConnection; end; function TDAEHETDriver.GetDescription: string; begin result := 'Connection for Heterogenous Queries across multiple databases'; end; function TDAEHETDriver.GetDriverID: string; begin result := 'HET'; end; { TDAEHETConnection } constructor TDAEHETConnection.Create(aDriver: TDAEDriver; aName: string = ''); begin inherited; fActiveConnections := TStringList.Create(); fActiveConnections.Sorted := true; fActiveConnections.Duplicates := dupError; end; function TDAEHETConnection.CreateCustomConnection: TCustomConnection; begin result := nil; end; destructor TDAEHETConnection.Destroy; var i: Integer; begin for i := 0 to fActiveConnections.Count-1 do fActiveConnections.Objects[i].Free(); FreeAndNil(fActiveConnections); //ToDo: auto-transacton handling? inherited; end; function TDAEHETConnection.DoBeginTransaction: Integer; var i: Integer; begin if fInTransaction then raise Exception.Create('Already in a transaction'); for i := 0 to fActiveConnections.Count-1 do (fActiveConnections.Objects[i] as TDAHETConnectionContainer).fConnection.BeginTransaction(); fInTransaction := true; result := 0; end; procedure TDAEHETConnection.DoCommitTransaction; var i: Integer; begin inherited; try for i := 0 to fActiveConnections.Count-1 do (fActiveConnections.Objects[i] as TDAHETConnectionContainer).fConnection.CommitTransaction(); except // ToDo: we need to use a two-phase commit here, to properly handle any single commit failing // to roll back ALL transactons (inclusing those already (tentatively) comitted. DoRollbackTransaction(); raise; end; fInTransaction := false; end; procedure TDAEHETConnection.DoRollbackTransaction; var i: Integer; begin inherited; for i := 0 to fActiveConnections.Count-1 do (fActiveConnections.Objects[i] as TDAHETConnectionContainer).fConnection.RollbackTransaction(); fInTransaction := false; end; function TDAEHETConnection.DoGetInTransaction: Boolean; begin result := fInTransaction; end; function TDAEHETConnection.GetConnected: Boolean; begin result := true; end; function TDAEHETConnection.GetConnectionForObject(const aObjectName: string; aOpenConnection: boolean = false): IDAConnection; var lConnectionName: string; begin lConnectionName := (ConnectionDefinition as TDAHETConnection).ObjectMappings.Values[aObjectName]; if lConnectionName = '' then raise Exception.CreateFmt('HET Connection "%s" does not contain an object mapping for "%s".', [ConnectionName, aObjectName]); result := GetConnectionByName(lConnectionName); end; function TDAEHETConnection.GetConnectionByName(const aConnectionName: string; aOpenConnection: boolean = false): IDAConnection; var i: Integer; begin if aConnectionName = self.ConnectionName then raise Exception.CreateFmt('Cannot use HET Connection "%s" nested within itself.', [ConnectionName]); i := fActiveConnections.IndexOf(aConnectionName); if i >= 0 then begin result := (fActiveConnections.Objects[i] as TDAHETConnectionContainer).fConnection end else begin result := ConnectionManager.NewConnection(aConnectionName, true); //todo: aOpenConnection if fInTransaction then result.BeginTransaction; fActiveConnections.AddObject(aConnectionName, TDAHETConnectionContainer.Create(result)); end; end; { TDAHETConnectionContainer } constructor TDAHETConnectionContainer.Create(aConnection: IDAConnection); begin fConnection := aConnection; end; { global driver registration } var _driver: TDAEDriver = nil; procedure Register; begin RegisterComponents(DAPalettePageName, [TDAHETDriver]); end; function GetDriverObject: IDADriver; begin if (_driver = nil) then _driver := TDAEHETDriver.Create(nil); result := _driver; end; exports GetDriverObject name func_GetDriverObject; initialization _driver := nil; RegisterDriverProc(GetDriverObject); finalization UnregisterDriverProc(GetDriverObject); FreeAndNIL(_driver); end.