unit Aurelius.Schema.AbsoluteDB;

{$I Aurelius.Inc}

interface

uses
  ABSMain, ABSTypes,
  Aurelius.Drivers.AbsoluteDB,
  Aurelius.Drivers.Interfaces,
  Aurelius.Schema.AbstractImporter,
  Aurelius.Sql.Metadata;

type
  TAbsoluteDBSchemaImporter = class(TAbstractSchemaImporter)
  strict protected
    procedure GetDatabaseMetadata(Connection: IDBConnection; Database: TDatabaseMetadata); override;
  end;

  TAbsoluteDBSchemaRetriever = class(TSchemaRetriever)
  strict private
    FABSTable: TABSTable;
    function ABSDatabase: TABSDatabase;
    procedure GetTables;
    procedure GetColumns;
    procedure GetKeys;
    procedure GetFieldDefinition(Column: TColumnMetadata; ADatatype: TABSAdvancedFieldType; ASize: Integer);
  public
    constructor Create(AConnection: IDBConnection; ADatabase: TDatabaseMetadata); override;
    destructor Destroy; override;
    procedure RetrieveDatabase; override;
  end;

implementation

uses
  DB,
  Classes,
  SysUtils,
  Aurelius.Schema.Register,
  Aurelius.Schema.Utils;

{ TAbsoluteDBSchemaImporter }

procedure TAbsoluteDBSchemaImporter.GetDatabaseMetadata(Connection: IDBConnection;
  Database: TDatabaseMetadata);
var
  Retriever: TSchemaRetriever;
begin
  Retriever := TAbsoluteDBSchemaRetriever.Create(Connection, Database);
  try
    Retriever.RetrieveDatabase;
  finally
    Retriever.Free;
  end;
end;

{ TAbsoluteDBSchemaRetriever }

function TAbsoluteDBSchemaRetriever.ABSDatabase: TABSDatabase;
begin
  Result := (Connection as TAbsoluteDBConnectionAdapter).ABSDatabase;
end;

constructor TAbsoluteDBSchemaRetriever.Create(AConnection: IDBConnection;
  ADatabase: TDatabaseMetadata);
begin
  inherited Create(AConnection, ADatabase);
  FABSTable := TABSTable.Create(nil);
  FABSTable.DatabaseName := ABSDatabase.DatabaseName;
end;

destructor TAbsoluteDBSchemaRetriever.Destroy;
begin
  FABSTable.Free;
  inherited;
end;

procedure TAbsoluteDBSchemaRetriever.GetColumns;
var
  Table: TTableMetadata;
  Column: TColumnMetadata;
  I: integer;
begin
  for Table in Database.Tables do
  begin
    FABSTable.Close;
    FABSTable.TableName := Table.Name;
    FABSTable.FieldDefs.Update;

    {Get fields}
    for I := 0 to FABSTable.AdvFieldDefs.Count - 1 do
    begin
      Column := TColumnMetadata.Create(Table);
      Table.Columns.Add(Column);
      Column.Name := FABSTable.AdvFieldDefs[I].Name;
      Column.NotNull := FABSTable.AdvFieldDefs[I].Required;

      GetFieldDefinition(Column,
        FABSTable.AdvFieldDefs[I].DataType,
        FABSTable.AdvFieldDefs[I].Size);

      Column.AutoGenerated := (Pos('autoinc', Lowercase(Column.DataType)) = 1);
    end;
  end;
end;

procedure TAbsoluteDBSchemaRetriever.GetFieldDefinition(Column: TColumnMetadata;
  ADatatype: TABSAdvancedFieldType; ASize: Integer);

const
  MapStr: array[TABSAdvancedFieldType] of string =
    (
    {aftUnknown          } '',
    {aftChar             } 'Char',
    {aftString           } 'Varchar',
    {aftWideChar         } 'WideString',
    {aftWideString       } 'WideString',
    {aftShortint         } 'SmallInt',
    {aftSmallint         } 'SmallInt',
    {aftInteger          } 'Integer',
    {aftLargeint         } 'LargeInt',
    {aftByte             } 'Byte',
    {aftWord             } 'Word',
    {aftCardinal         } 'Cardinal',
    {aftAutoInc          } 'AutoInc',
    {aftAutoIncShortint  } 'AutoInc(ShortInt)',
    {aftAutoIncSmallint  } 'AutoInc(SmallInt)',
    {aftAutoIncInteger   } 'AutoInc(Integer)',
    {aftAutoIncLargeint  } 'AutoInc(LargeInt)',
    {aftAutoIncByte      } 'AutoInc(Byte)',
    {aftAutoIncWord      } 'AutoInc(Word)',
    {aftAutoIncCardinal  } 'AutoInc(Cardinal)',
    {aftSingle           } 'Single',
    {aftDouble           } 'Float',
    {aftExtended         } 'Extended',
    {aftBoolean          } 'Logical',
    {aftCurrency         } 'Currency',
    {aftDate             } 'Date',
    {aftTime             } 'Time',
    {aftDateTime         } 'DateTime',
    {aftTimeStamp        } 'TimeStamp',
    {aftBytes            } 'Bytes',
    {aftVarBytes         } 'VarBytes',
    {aftBlob             } 'Blob',
    {aftGraphic          } 'Graphic',
    {aftMemo             } 'Memo',
    {aftFormattedMemo    } 'FormattedMemo',
    {aftWideMemo         } 'WideMemo',
    {aftGuid             } 'Guid'
    );
begin
  Column.DataType := MapStr[ADataType];
  if ADataType in [aftBytes, aftChar, aftString, aftWideChar, aftWideString] then
    Column.Length := ASize;
end;

procedure TAbsoluteDBSchemaRetriever.GetKeys;
var
  Table: TTableMetadata;
  UniqueKey: TUniqueKeyMetadata;
  DBIndex: TDBIndexMetadata;
  FieldNames: TStrings;
  I, J: integer;
begin
  for Table in Database.Tables do
  begin
    FABSTable.Close;
    FABSTable.TableName := Table.Name;
    FABSTable.IndexDefs.Update;

    {Get indexes}
    for I := 0 to FABSTable.IndexDefs.Count - 1 do
    begin
      if ixPrimary in FABSTable.IndexDefs[I].Options then
      begin
        FieldNames := TStringList.Create;
        try
          GetNamesList(FieldNames, FABSTable.AdvIndexDefs[I].Fields);
          for J := 0 to FieldNames.Count - 1 do
            Table.IdColumns.Add(TSchemaUtils.GetColumn(Table, FieldNames[J]));
        finally
          FieldNames.Free;
        end;
      end
      else
      if ixUnique in FABSTable.IndexDefs[I].Options then
      begin
        UniqueKey := TUniqueKeyMetadata.Create(Table);
        Table.UniqueKeys.Add(UniqueKey);
        UniqueKey.Name := FABSTable.IndexDefs[I].Name;
        FieldNames := TStringList.Create;
        try
          GetNamesList(FieldNames, FABSTable.AdvIndexDefs[I].Fields);
          for J := 0 to FieldNames.Count - 1 do
            UniqueKey.Columns.Add(TSchemaUtils.GetColumn(Table, FieldNames[J]));
        finally
          FieldNames.Free;
        end;
      end
      else
      begin
        DBIndex := TDBIndexMetadata.Create(Table);
        Table.DBIndexes.Add(DBIndex);
        DBIndex.Name := FABSTable.IndexDefs[I].Name;
        FieldNames := TStringList.Create;
        try
          GetNamesList(FieldNames, FABSTable.AdvIndexDefs[I].Fields);
          for J := 0 to FieldNames.Count - 1 do
            DBIndex.Columns.Add(TSchemaUtils.GetColumn(Table, FieldNames[J]));
        finally
          FieldNames.Free;
        end;
      end;
    end;
  end;
end;

procedure TAbsoluteDBSchemaRetriever.GetTables;
var
  TableNames: TStringList;
  I: Integer;
  Table: TTableMetadata;
begin
  TableNames := TStringList.Create;
  try
    ABSDatabase.GetTablesList(TableNames);
    for I := 0 to TableNames.Count - 1 do
    begin
      Table := TTableMetadata.Create(Database);
      Database.Tables.Add(Table);
      Table.Name := TableNames[I];
    end;
  finally
    TableNames.Free;
  end;
end;

procedure TAbsoluteDBSchemaRetriever.RetrieveDatabase;
begin
  Database.Clear;
  GetTables;
  GetColumns;
  GetKeys;
end;

initialization
  TSchemaImporterRegister.GetInstance.RegisterImporter('AbsoluteDB', TAbsoluteDBSchemaImporter.Create);

end.

