unit Aurelius.Drivers.MSSQL;

interface

uses
  SysUtils, Classes,
  Aurelius.Sql.MSSQL,
  Aurelius.Schema.MSSQL,
  Aurelius.Comp.Drivers,
  Aurelius.Drivers.Interfaces,
  Aurelius.Drivers.Odbc;

type
  TMSSQLConnection = class(TOdbcNativeConnectionAdapter)
  strict private
    const NoLoginTimeout = -2;
  strict private
    FLoginTimeout: Integer;
  strict protected
    procedure DoConnect; override;
  public
    constructor Create(const AConnectionString: string); reintroduce; overload;
    constructor Create(Params: IDriverParams); overload;
    function SqlDialect: string; override;
  end;

var
  MSSQLOdbcDrivers: TArray<string>;

implementation

uses
  Aurelius.Drivers.Odbc.Classes;

{ TMSSQLConnection }

constructor TMSSQLConnection.Create(Params: IDriverParams);
var
  Driver: string;
  Server: string;
  Database: string;
  UserName: string;
  Password: string;
  TrustedConnection: Boolean;
  MARS: Boolean;
  OdbcAdvanced: string;

  Conn: string;
begin
  inherited Create;

  // Retrieve parameters
  Server := Params.GetAsString('Server');
  Database := Params.GetAsString('Database');
  UserName := Params.GetAsString('UserName');
  Password := Params.GetAsString('Password');
  TrustedConnection := Params.GetAsBoolean('TrustedConnection');
  MARS := Params.GetAsBoolean('MARS', True);
  OdbcAdvanced := Params.GetAsString('OdbcAdvanced');
  FLoginTimeout := Params.GetAsInteger('LoginTimeout', NoLoginTimeout);

  // Validate parameters
  if Server = '' then
    raise EInvalidDriverParam.Create('MSSQL', 'Param "Server" not specified');
  if Database = '' then
    raise EInvalidDriverParam.Create('MSSQL', 'Param "Database" not specified');

  // Find driver
  Driver := TOdbcEnvironment.Global.MatchDriver(MSSQLOdbcDrivers);
  if Driver = '' then
    raise EInvalidDriverParam.Create('MSSQL', 'Could not find a valid Odbc driver');

  // Build connection string
  Conn := Format('Driver={%s};Server=%s;Database=%s', [Driver, Server, Database]);
  if UserName <> '' then
    Conn := Conn + ';Uid=' + UserName;
  if Password <> '' then
    Conn := Conn + ';Pwd=' + Password;
  if TrustedConnection then
    Conn := Conn + ';Trusted_Connection=Yes';
  if MARS then
    Conn := Conn + ';MARS_Connection=Yes';
  if OdbcAdvanced <> '' then
    Conn := Conn + ';' + OdbcAdvanced;

  ConnectionString := Conn;
end;

procedure TMSSQLConnection.DoConnect;
begin
  if FLoginTimeout <> NoLoginTimeout then
    Connection.SetLoginTimeout(FLoginTimeout);
  inherited DoConnect;
end;

function TMSSQLConnection.SqlDialect: string;
begin
  Result := 'MSSQL';
end;

constructor TMSSQLConnection.Create(const AConnectionString: string);
begin
  Create(IDriverParams(TDriverParams.Create(AConnectionString)));
end;

procedure RegisterDriverAdapter;
var
  Driver: IAureliusDriver;
begin
  Driver := TAureliusDriver.Create('MSSQL');
  Driver.ParamDefs.Add('Server', True);
  Driver.ParamDefs.Add('Database', True);
  Driver.ParamDefs.Add('UserName');
  Driver.ParamDefs.Add('Password');
  Driver.ParamDefs.AddBoolean('TrustedConnection');
  Driver.ParamDefs.AddBoolean('MARS');
  Driver.ParamDefs.Add('OdbcAdvanced');
  Driver.ParamDefs.Add('LoginTimeout');

  Driver.DriverFactory :=
    function(Args: TDriverFactoryArgs): IDBConnection
    begin
      Result := TMSSQLConnection.Create(Args.Params);
    end;

  TAureliusDriverRegister.Instance.RegisterDriver(Driver);
  TAureliusDriverRegister.Instance.RegisterDriverUnits(Driver.Name, 'Aurelius.Drivers.MSSQL');
end;

procedure AddMSSQLDrivers;
begin
  MSSQLOdbcDrivers := TArray<string>.Create(
    'ODBC Driver 17 for SQL Server',
    'ODBC Driver 13.1 for SQL Server',
    'ODBC Driver 13 for SQL Server',
    'ODBC Driver 11 for SQL Server',
    'SQL Server Native Client 11.0',
    'SQL Server Native Client 10.0',
    'SQL Native Client',
    'SQL Server'
  );
end;

initialization
  AddMSSQLDrivers;
  RegisterDriverAdapter;
end.
