unit SQLStringsPropertyEditorDlg;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, strutils,
  SynEdit, ButtonPanel, SynHighlighterSQL, ComCtrls, SQLDb, db, DBGrids, Menus,
  SrcEditorIntf, clipbrd, StdCtrls, fpsqltree, fpsqlparser;

type

  { TSQLStringsPropertyEditorDlg }

  TSQLStringsPropertyEditorDlg = class(TForm)
    ButtonsPanel: TButtonPanel;
    CbxMetaData: TComboBox;
    MIPaste: TMenuItem;
    MetaDBGrid: TDBGrid;
    EdtObject: TEdit;
    ImageList: TImageList;
    Label1: TLabel;
    MIMeta: TMenuItem;
    MIMetaColumns: TMenuItem;
    MICheck: TMenuItem;
    MICreateConstant: TMenuItem;
    MICleanup: TMenuItem;
    PMSQL: TPopupMenu;
    PMMeta: TPopupMenu;
    ResultDBGrid: TDBGrid;
    SQLDataSource: TDatasource;
    OpenDialog: TOpenDialog;
    PageControl: TPageControl;
    SaveDialog: TSaveDialog;
    SQLDataSource1: TDatasource;
    SQLEditor: TSynEdit;
    SQLHighlighter: TSynSQLSyn;
    EditorTabSheet: TTabSheet;
    ResultTabSheet: TTabSheet;
    SQLQuery: TSQLQuery;
    MetaTabSheet: TTabSheet;
    SQLMeta: TSQLQuery;
    TBConst: TToolButton;
    ToolBar: TToolBar;
    OpenToolButton: TToolButton;
    SaveToolButton: TToolButton;
    DividerToolButton: TToolButton;
    ExecuteToolButton: TToolButton;
    TBCheck: TToolButton;
    procedure MetaDBGridDblClick(Sender: TObject);
    procedure ExecuteToolButtonClick(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure MICleanupClick(Sender: TObject);
    procedure MICreateConstantClick(Sender: TObject);
    procedure MIMetaColumnsClick(Sender: TObject);
    procedure MIPasteClick(Sender: TObject);
    procedure OpenToolButtonClick(Sender: TObject);
    procedure SaveToolButtonClick(Sender: TObject);
    procedure SQLEditorMouseDown(Sender: TObject; Button: TMouseButton;
      {%H-}Shift: TShiftState; X, Y: Integer);
    procedure TBCheckClick(Sender: TObject);
    procedure TBConstClick(Sender: TObject);
  private
    { private declarations }
    FMetaFromSynedit: Boolean;
    FConnection:TSQLConnection;
    FISSQLScript: Boolean;
    FTransaction:TSQLTransaction;
    FWordUnderCursor:string;
    function CheckConnection:boolean;
    procedure CheckSQLSyntax({%H-}SQL: TStrings);
    procedure CleanupDelphiCode;
    procedure CreateConstant;
    procedure ShowMetaData;
  public
    { public declarations }
    constructor Create(AOwner:TComponent);override;
  published
    property Connection: TSQLConnection   read FConnection  write FConnection;
    property Transaction:TSQLTransaction read FTransaction write FTransaction;
    Property IsSQLScript : Boolean Read FISSQLScript Write FIsSQLScript;
  end; 

implementation

{$R *.lfm}

resourcestring
  SResultTabCaption = 'Results';
  SSQLTabCaption    = 'SQL Code';
  SMetaTabCaption   = 'Metadata';
  SMetaTables       = 'Tables';
  SMetaColumns      = 'Columns';
  SMetaProcedures   = 'Procedures';
  SMetaPleaseSpecifyATableInTheObjectField = 'Please specify a table in the '
    +'object field.';
  SMetaSysTables    = 'SysTables';

  SLoadSQLCodeHint = 'Load SQL code ...';
  SSaveSQLCodeHint = 'Save SQL code ...';
  SRunSQLCodeHint = 'Run SQL code';
  SQuickCheckOfSQLSyntaxHint = 'Quick check of SQL syntax';

  // SQL Parser results:
  // Note: sql parser is not quite exact, so indicate it's not completely sure
  SSQLOK            = 'Quick SQL check OK';
  SQLSyntaxOK       = 'No syntax errors in SQL statement found.';
  SSQLError         = 'Probable SQL error';
  SSQLSyntaxError   = 'Probable syntax error in SQL statement:'+slineBreak+'%s';

{ TSQLStringsPropertyEditorDlg }

//----------------------------------------------------------------//
constructor TSQLStringsPropertyEditorDlg.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  SourceEditorManagerIntf.GetEditorControlSettings(SQLEditor);
  SourceEditorManagerIntf.GetHighlighterSettings(SQLHighlighter);
  EditorTabSheet.Caption := SSQLTabCaption;
  ResultTabSheet.Caption := SResultTabCaption;
  MetaTabSheet.Caption := SMetaTabCaption;
  OpenToolButton.Hint := SLoadSQLCodeHint;
  SaveToolButton.Hint := SSaveSQLCodeHint;
  ExecuteToolButton.Hint := SRunSQLCodeHint;
  TBCheck.Hint := SQuickCheckOfSQLSyntaxHint;
  CbxMetaData.Items.Add(SMetaTables);
  CbxMetaData.Items.Add(SMetaSysTables);
  CbxMetaData.Items.Add(SMetaColumns);
  CbxMetaData.Items.Add(SMetaProcedures);
end;

//----------------------------------------------------------//
function TSQLStringsPropertyEditorDlg.CheckConnection:boolean;
begin
  Result := (Assigned(FConnection)) and (Assigned(FTransaction))
             and(FConnection.Connected);
end;

//------------------------------------------------------------------------//
procedure TSQLStringsPropertyEditorDlg.OpenToolButtonClick(Sender: TObject);
begin
  if(OpenDialog.Execute)then
    SQLEditor.Lines.LoadFromFile(OpenDialog.FileName);
end;

//---------------------------------------------------------------------------//
procedure TSQLStringsPropertyEditorDlg.ExecuteToolButtonClick(Sender: TObject);
begin
  FMetaFromSynedit:=Sender.ClassNameIs('TMenuItem');
  if PageControl.ActivePage=MetaTabSheet then
    ShowMetaData
  else
    try
      SQLQuery.Close;
      SQLQuery.SQL.Text := SQLEditor.Text;
      SQLQuery.Open;
      PageControl.ActivePage := ResultTabSheet;
    except
      on e:Exception do
        MessageDlg(e.Message, mtError, [mbOK], 0);
    end;
end;

procedure TSQLStringsPropertyEditorDlg.MetaDBGridDblClick(Sender: TObject);
begin
  if assigned(MetaDBGrid.SelectedField) and (MetaDBGrid.SelectedField.Value <> NULL) then
    if FMetaFromSynedit then
      begin
      MIPasteClick(Sender);
      end
    else
      EdtObject.Text:=MetaDBGrid.SelectedField.AsString;
end;

//-------------------------------------------------------------//
procedure TSQLStringsPropertyEditorDlg.FormShow(Sender: TObject);

Var
  D : TSQLDialect;

begin
  TBCheck.Visible:=True;
  MICheck.Visible:=True;
  D:=sqlStandard;
  If Assigned(FConnection) then
    begin
    if (copy(LowerCase(FConnection.ClassName),1,3)='tib') then
      D:=sqlinterbase6
    else if (copy(LowerCase(FConnection.ClassName),1,7)='toracle') then
      D:=sqloracle
    else if (Copy(LowerCase(FConnection.ClassName),1,6)='tmysql') then
      D:=sqlmysql;
    end;
  if (CheckConnection) then
    begin
    SQLQuery.DataBase    := FConnection;
    SQLQuery.Transaction := FTransaction;
    SQLMeta.DataBase    := FConnection;
    SQLMeta.Transaction := FTransaction;
    ResultTabSheet.TabVisible    := True;
    MetaTabSheet.TabVisible    := True;
    ExecuteToolButton.Visible := True;
    FConnection.GetTableNames(SQLHighLighter.TableNames);
    end
  else
    begin
    ResultTabSheet.TabVisible    := False;
    MetaTabSheet.TabVisible    := False;
    ExecuteToolButton.Visible := False;
    end;
  SQLHighlighter.SQLDIalect:=D;
  SQLHighlighter.Enabled:=True;
  CbxMetaData.ItemIndex:=0;
{$ifdef unix}
  // keep this only because of gtk1
  {$ifdef LCLGtk}
  SQLEditor.Font.Name:='-adobe-courier-medium-r-normal-*-8-*-*-*-m-*-iso10646-1';
  {$endif}
{$endif}
  SQLEditor.SetFocus;
end;

procedure TSQLStringsPropertyEditorDlg.MICleanupClick(Sender: TObject);

begin
  CleanupDelphiCode;
end;

procedure TSQLStringsPropertyEditorDlg.CleanupDelphiCode;

Var
  L : TStringList;
  I,J,K : Integer;
  S : String;
begin
  L:=TStringList.Create;
  try
    L.Assign(SQLEditor.Lines);
    For I:=0 to L.Count-1 do
      begin
      S:=L[i];
      K:=0;
      For J:=1 to Length(S) do
        If S[j]='''' then
          Inc(K);
      if (K<>0) and ((K mod 2)=0) then
        begin
        J:=Pos('''',S);
        Delete(S,1,J);
        J:=RPos('''',S);
        S:=Copy(S,1,J-1);
        L[i]:=S;
        end;
      end;
    SQLEditor.Lines:=L;
  finally
    L.Free;
  end;
end;

procedure TSQLStringsPropertyEditorDlg.MICreateConstantClick(Sender: TObject);
begin
  CreateConstant;
end;

procedure TSQLStringsPropertyEditorDlg.MIMetaColumnsClick(Sender: TObject);
begin
  if FWordUnderCursor<>'' then
    begin
    CbxMetaData.ItemIndex:=2; //stColumns
    EdtObject.Text:=FWordUnderCursor;
    PageControl.ActivePage:=MetaTabSheet;
    ExecuteToolButtonClick(Sender);
    end;
end;

procedure TSQLStringsPropertyEditorDlg.MIPasteClick(Sender: TObject);

begin
  if assigned(MetaDBGrid.SelectedField) and (MetaDBGrid.SelectedField.Value <> NULL) then
    begin
    SQLEditor.InsertTextAtCaret(' '+TSQLConnection(SQLMeta.DataBase).FieldNameQuoteChars[0]+
      trim(MetaDBGrid.SelectedField.AsString)+TSQLConnection(SQLMeta.DataBase).FieldNameQuoteChars[1]);
    PageControl.ActivePage:=EditorTabSheet;
    end;
end;

procedure TSQLStringsPropertyEditorDlg.CreateConstant;

Var
  C,S : String;
  I : Integer;

begin
  C:='';
  For I:=0 to SQLEditor.Lines.Count-1 do
    begin
    S:=SQLEditor.Lines[i];
    If (C<>'') then
      C:=C+'+LineEnding+'+LineEnding;
    C:=C+''''+StringReplace(S,'''','''''',[rfReplaceAll])+'''';
    end;
  C:='SQL = '+C+';';
  Clipboard.AsText:=C;
end;

procedure TSQLStringsPropertyEditorDlg.ShowMetaData;
var
  SchemaType:TSchemaType;
begin
  case CbxMetaData.ItemIndex of
    0:SchemaType:=stTables;
    2:begin
        SchemaType:=stColumns;
        if EdtObject.Text='' then
          begin
          ShowMessage(SMetaPleaseSpecifyATableInTheObjectField);
          exit;
          end;
      end;
    3:SchemaType:=stProcedures;
    1:SchemaType:=stSysTables;
    else
      SchemaType:=stNoSchema;
  end;
  if SchemaType<>stNoSchema then
    try
      SQLMeta.Close;
      SQLMeta.SetSchemaInfo(SchemaType,EdtObject.Text,'');
      SQLMeta.Open;
    except
      on e:Exception do
        MessageDlg(e.Message, mtError, [mbOK], 0);
    end;
end;

//------------------------------------------------------------------------//
procedure TSQLStringsPropertyEditorDlg.SaveToolButtonClick(Sender: TObject);
begin
  if(SaveDialog.Execute)then
    SQLEditor.Lines.SaveToFile(SaveDialog.FileName);
end;

procedure TSQLStringsPropertyEditorDlg.SQLEditorMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  MPos,Caret:tpoint;

begin
  If Button=mbRight then // save word under cursor
    begin
    FWordUnderCursor:='';
    MPos.x:=x;
    MPos.y:=y;
    Caret:=SQLEditor.PhysicalToLogicalPos(SQLEditor.PixelsToLogicalPos(MPos));
    FWordUnderCursor:=SQLEditor.GetWordAtRowCol(Caret);
    end;
end;

procedure TSQLStringsPropertyEditorDlg.TBCheckClick(Sender: TObject);
begin
  CheckSQLSyntax(SQLEditor.Lines);
end;

procedure TSQLStringsPropertyEditorDlg.TBConstClick(Sender: TObject);
begin
  CreateConstant;
end;

procedure TSQLStringsPropertyEditorDlg.CheckSQLSyntax(SQL: TStrings);
Var
  S : TStream;
  P : TSQLParser;
  E : TSQLElement;
  EL : TSQLElementList;
  Msg : String;
begin
  S:=TMemoryStream.Create;
  try
    SQL.SaveToStream(S);
    S.Position:=0;
    if S.Size=0 then
      exit; // no message for empty input
    P:=TSQLParser.Create(S);
    try
      try
        If IsSQLScript then
        begin
          EL:=P.ParseScript;
          EL.Free;
        end
        else begin
          E:=P.Parse;
          E.Free;
        end;
        MessageDLG(SSQLOK,SQLSyntaxOK,mtInformation,[mbOK],0);
      except
        On E : Exception do
          begin
          Msg:=Format(SSQLSyntaxError,[E.Message]);
          MessageDLG(SSQLError,Msg,mtError,[mbOK],0);
          end;
      end;
    finally
      P.Free;
    end;
  finally
    S.Free;
  end;
end;

end.

