unit uSchemaUtilsServer;

interface

uses
  Classes, SysUtils,
  uROClientIntf, uROTypes, uROServer, uROServerIntf, uROSessions,
  DataAbstract4_Intf, DataAbstractService_Impl,
  uDAClasses, uDAInterfaces, uDADataTable;

type
  TReplicarDataSet = class
  protected
    function GenerateNewSQL : String;
  public
    Service : TDataAbstractService;
    Columnas : String;
    ColumnasQueSuman : String;
    ColumnasQueAVG : String;
    SourceDataSetName : String;
    function GetNewDataSet : IDADataset;
  end;


implementation

{ TReplicaDataSet }

uses
  RegExpr,
  Dialogs, JclStrings;

function TReplicarDataSet.GenerateNewSQL: String;
var
  AColumnList : TStringList;
  ASQL: TStringList;
  ds : IDADataset;
  i : integer;
  Separador : String;
  bHayAgrupacion : Boolean;

  function GetTrueFieldName(AField : string; ConAlias : Boolean = True) : String;
  var
    ARegExpr : TRegExpr;
  begin
    Result := AField; // Por si el campo no tuviera alias
    ARegExpr := TRegExpr.Create;
    try
      ARegExpr.ModifierI := True;   // case insensitive
      ARegExpr.InputString := ds.SQL;
      ARegExpr.Expression := '[^ ,]+[ ]+AS[ ]+' + AField; // buscar (xxxx.xxxx AS AField) o (xxxx AS AField)
      ARegExpr.Expression := ARegExpr.Expression + '|';   // o
      ARegExpr.Expression := ARegExpr.Expression + '[^ ,]+\.' + AField; // buscar xxxx.AField
      if ARegExpr.Exec then
        Result := ARegExpr.Match[0];

      if not ConAlias then
      begin
        ARegExpr.InputString := Result;
        ARegExpr.Expression := '^[^ ,]+'; // Para quedarme con el campo sin alias
        if ARegExpr.Exec then
          Result := ARegExpr.Match[0]
      end;
    finally
      FreeAndNil(ARegExpr);
    end;
  end;

begin
  AColumnList := TStringList.Create;
  ASQL := TStringList.Create;
  ds := Service.ServiceSchema.NewDataset(Service.Connection, SourceDataSetName);
  bHayAgrupacion := False;
  try
    AColumnList.CommaText := Columnas;

    with ASQL do
    begin
      Insert(0, 'SELECT DISTINCT');
      Insert(1, ''); // Lista de campos
      Insert(2, Copy(ds.SQL, Pos('FROM', ds.SQL), Length(ds.SQL)));
      Insert(3, ''); // Group by
      Separador := '';

      for i := 0 to AColumnList.Count - 1 do
      begin
        if Assigned(ds.FindField(AColumnList[i])) then
        begin
          if i > 0 then
            Separador := ',';

          if Pos('#'+AColumnList[i]+'#', ColumnasQueSuman) > 0 then
          begin
            Strings[1] := Strings[1] + Separador + 'SUM(' + GetTrueFieldName(AColumnList[i], False) + ') AS ' + AColumnList[i];
            bHayAgrupacion := True;
          end
          else if Pos('#'+AColumnList[i]+'#', ColumnasQueAVG) > 0 then
          begin
            Strings[1] := Strings[1] + Separador + 'AVG(' + GetTrueFieldName(AColumnList[i], False) + ') AS ' + AColumnList[i];
            bHayAgrupacion := True;
          end
          else begin
            Strings[1] := Strings[1] + Separador + GetTrueFieldName(AColumnList[i]);
            if Length(Strings[3]) = 0 then
              Strings[3] := 'GROUP BY '
            else begin
              if (Strings[3] <> 'GROUP BY ') then
                Strings[3] := Strings[3] + Separador;
            end;
            Strings[3] := Strings[3] + GetTrueFieldName(AColumnList[i], False);
          end;
        end;
      end;  // for

      if not bHayAgrupacion then
        Delete(3);
    end;
    Result := ASQL.Text;

  finally
    AColumnList.Free;
    ASQL.Free;
  end;
end;



function TReplicarDataSet.GetNewDataSet: IDADataset;
var
  SQL: String;
  ASchemaDataSet: TDADataSet;
  AColumnList : TStringList;
  i, j : Integer;
begin
  SQL := GenerateNewSQL;
  Result := Service.Connection.NewDataset(SQL, SourceDataSetName);

  ASchemaDataSet := Service.ServiceSchema.Datasets.DatasetByName(SourceDataSetName);
  if Assigned(ASchemaDataSet) then
  begin
    Result.Params.AssignParamCollection(ASchemaDataSet.Params);  
    Result.Fields.AssignFieldCollection(ASchemaDataSet.Fields);
    AColumnList := TStringList.Create;
    try
      AColumnList.CommaText := Columnas;
      j := 0;
      i := Result.Fields.Count;
      while i > 0 do
      begin
        if AColumnList.IndexOf(Result.Fields[j].Name) = -1 then
          Result.Fields.Delete(j)
        else
          j := j + 1; // Un campo que dejo
        i := i - 1; // Un campo menos por revisar
      end;
    finally
      AColumnList.Free;
    end;
  end;

  Result.Close;
  Result.Where.Clear;
end;

end.
