unit Aurelius.Sql.ElevateDB;

{$I Aurelius.Inc}

interface

uses
  DB,
  Aurelius.Sql.AnsiSqlGenerator,
  Aurelius.Sql.BaseTypes,
  Aurelius.Sql.Commands,
  Aurelius.Sql.Interfaces,
  Aurelius.Sql.Metadata,
  Aurelius.Sql.Register;

type
  TElevateDBSQLGenerator = class(TAnsiSQLGenerator)
  protected
    function GetMaxConstraintNameLength: Integer; override;
    procedure DefineColumnType(Column: TColumnMetadata); override;
    function GetSqlLiteral(AValue: Variant; AType: TFieldType): string; override;
    function GetGeneratorName: string; override;
    function GetSqlDialect: string; override;
    function GetSupportedFeatures: TDBFeatures; override;
    function GenerateGetLastInsertId(SQLField: TSQLField): string; override;
    function GenerateDropField(Column: TColumnMetadata): string; override;
    function GenerateLimitedSelect(SelectSql: TSelectSql; Command: TSelectCommand): string; override;

    function GetSupportedFieldTypes: TFieldTypeSet; override;
  public
    constructor Create;
  end;

implementation

uses
  Variants, SysUtils,
  Aurelius.Sql.Functions;

{ TElevateDBSQLGenerator }

constructor TElevateDBSQLGenerator.Create;
begin
  inherited Create;
  RegisterFunction('bytelength', TSimpleSQLFunction.Create('length', TypeInfo(integer)));
end;

procedure TElevateDBSQLGenerator.DefineColumnType(Column: TColumnMetadata);
begin
  DefineNumericColumnType(Column);
  if Column.DataType <> '' then
  begin
    // Ignore Precision and Scale if Scale > 4 (not supported by ElevateDB)
    // In this case, use a built-in numeric type to hold the specified scale
    if Column.Scale >= 4 then
    begin
      Column.DataType := 'FLOAT';
      Column.Precision := 0;
      Column.Scale := 0;
    end;

    // All precisions in ElevateDB are stored as 19. So we will set it here as 19 to match
    // the schema when reverse engineering is performed
    if Column.Precision <> 0 then
      Column.Precision := 19;
    Exit;
  end;

  case Column.FieldType of
    ftWideString:
      Column.DataType := 'VARCHAR($len)';
    ftFixedWideChar:
      Column.DataType := 'CHAR($len)';


    ftDateTime:
      Column.DataType := 'TIMESTAMP';
    ftBoolean:
      Column.DataType := 'BOOLEAN';

    ftFloat:
      Column.DataType := 'FLOAT';
    ftSingle:
      Column.DataType := 'FLOAT';
    ftExtended:
      Column.DataType := 'FLOAT';

    ftMemo:
      Column.DataType := 'CLOB';
    ftWideMemo:
      Column.DataType := 'CLOB';
    ftBlob:
      Column.DataType := 'BLOB';
    ftGuid:
      Column.DataType := 'GUID'
  else
    inherited DefineColumnType(Column);
  end;
  if Column.AutoGenerated then
    Column.DataType := Column.DataType + ' GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1)';

  // All precisions in ElevateDB are stored as 19. So we will set it here as 19 to match
  // the schema when reverse engineering is performed
  if Column.Precision <> 0 then
    Column.Precision := 19;
end;

function TElevateDBSQLGenerator.GenerateDropField(Column: TColumnMetadata): string;
begin
  result := InternalGenerateDropField(Column, True);
end;

function TElevateDBSQLGenerator.GenerateGetLastInsertId(SQLField: TSQLField): string;
begin
  Result := Format('SELECT LASTIDENTITY(''%s'', ''%s'')',
    [IdName(SQLField.Table.Name), IdName(SQLField.Field)]);

end;

function TElevateDBSQLGenerator.GenerateLimitedSelect(SelectSql: TSelectSql;
  Command: TSelectCommand): string;
begin
  Result := GenerateRegularSelect(SelectSql) + sqlLineBreak;
  if Command.HasMaxRows then
    Result := Result + Format('RANGE %d TO %d', [Command.FirstRow + 1, Command.LastRow + 1])
  else
    Result := Result + Format('RANGE %d', [Command.FirstRow + 1]);
end;

function TElevateDBSQLGenerator.GetSqlDialect: string;
begin
  Result := 'ElevateDB';
end;

function TElevateDBSQLGenerator.GetGeneratorName: string;
begin
  Result := 'ElevateDB SQL Generator';
end;

function TElevateDBSQLGenerator.GetMaxConstraintNameLength: Integer;
begin
  Result := 40;
end;

function TElevateDBSQLGenerator.GetSqlLiteral(AValue: Variant;
  AType: TFieldType): string;
begin
  Result := inherited GetSqlLiteral(AValue, AType);
end;

function TElevateDBSQLGenerator.GetSupportedFeatures: TDBFeatures;
begin
  Result := AllDBFeatures - [TDBFeature.Sequences, TDBFeature.RetrieveIdOnInsert, TDBFeature.Schemas];
end;

function TElevateDBSQLGenerator.GetSupportedFieldTypes: TFieldTypeSet;
begin
  Result := inherited GetSupportedFieldTypes;
  Result := Result + [ftBoolean, ftGuid];
end;

initialization
  TSQLGeneratorRegister.GetInstance.RegisterGenerator(TElevateDBSQLGenerator.Create);

end.
