diff --git a/heidisql.lpi b/heidisql.lpi index 5d4a471b..be251195 100644 --- a/heidisql.lpi +++ b/heidisql.lpi @@ -280,6 +280,13 @@ + + + + + + + diff --git a/heidisql.lpr b/heidisql.lpr index d75e2f51..a0b29cea 100644 --- a/heidisql.lpr +++ b/heidisql.lpr @@ -20,7 +20,7 @@ uses column_selection, loaddata, csv_detector, createdatabase, editvar, copytable, exportgrid, usermanager, selectdbobject, reformatter, searchreplace, connections, jsonregistry, sqlhelp, updatecheck, insertfiles, texteditor, - customize_highlighter, preferences, table_editor; + customize_highlighter, preferences, table_editor, view; {$R *.res} {.$R resources.rc} diff --git a/source/main.pas b/source/main.pas index ef34fe9b..fc420379 100644 --- a/source/main.pas +++ b/source/main.pas @@ -1421,7 +1421,7 @@ implementation uses FileInfo, winpeimagereader, elfreader, machoreader, About, data_sorting, column_selection, loaddata, editvar, copytable, csv_detector, exportgrid, usermanager, selectdbobject, reformatter, connections, sqlhelp, updatecheck, - insertfiles, texteditor, preferences, table_editor; + insertfiles, texteditor, preferences, table_editor, view; {$R *.lfm} @@ -12190,7 +12190,7 @@ begin FreeAndNil(ActiveObjectEditor); case Obj.NodeType of lntTable: EditorClass := TfrmTableEditor; - //lntView: EditorClass := TfrmView; + lntView: EditorClass := TfrmView; //lntProcedure, lntFunction: EditorClass := TfrmRoutineEditor; //lntTrigger: EditorClass := TfrmTriggerEditor; //lntEvent: EditorClass := TfrmEventEditor; diff --git a/source/view.lfm b/source/view.lfm new file mode 100644 index 00000000..41f66e99 --- /dev/null +++ b/source/view.lfm @@ -0,0 +1,423 @@ +object frmView: TfrmView + Left = 0 + Height = 625 + Top = 0 + Width = 875 + ClientHeight = 625 + ClientWidth = 875 + DesignTimePPI = 120 + ParentFont = False + TabOrder = 0 + object lblSelect: TLabel + Left = 4 + Height = 20 + Top = 186 + Width = 113 + BorderSpacing.Around = 6 + Caption = 'Select statement:' + end + object lblDisabledWhy: TLabel + AnchorSideLeft.Control = btnSave + AnchorSideLeft.Side = asrBottom + AnchorSideRight.Control = Owner + AnchorSideRight.Side = asrBottom + AnchorSideBottom.Control = Owner + AnchorSideBottom.Side = asrBottom + Left = 306 + Height = 31 + Top = 588 + Width = 563 + Anchors = [akLeft, akRight, akBottom] + AutoSize = False + BorderSpacing.Around = 6 + Caption = 'You need the SHOW VIEW privilege in order to edit a view.' + Layout = tlCenter + Visible = False + end + inline SynMemoBody: TSynEdit + AnchorSideLeft.Control = Owner + AnchorSideTop.Control = PageControlMain + AnchorSideTop.Side = asrBottom + AnchorSideRight.Control = Owner + AnchorSideRight.Side = asrBottom + AnchorSideBottom.Control = btnSave + Left = 6 + Height = 349 + Top = 233 + Width = 863 + BorderSpacing.Around = 6 + Anchors = [akTop, akLeft, akRight, akBottom] + Font.Color = clWindowText + Font.Height = -13 + Font.Name = 'Courier New' + Font.Pitch = fpFixed + Font.Quality = fqNonAntialiased + ParentColor = False + ParentFont = False + TabOrder = 1 + Gutter.LeftOffset = 2 + Gutter.Width = 70 + Gutter.MouseActions = <> + RightGutter.Width = 0 + RightGutter.MouseActions = <> + Keystrokes = <> + MouseActions = <> + MouseTextActions = <> + MouseSelActions = <> + Options = [eoAutoIndent, eoGroupUndo, eoShowScrollHint] + VisibleSpecialChars = [vscSpace, vscTabAtLast] + RightEdge = 0 + SelectedColor.BackPriority = 50 + SelectedColor.ForePriority = 50 + SelectedColor.FramePriority = 50 + SelectedColor.BoldPriority = 50 + SelectedColor.ItalicPriority = 50 + SelectedColor.UnderlinePriority = 50 + SelectedColor.StrikeOutPriority = 50 + BracketHighlightStyle = sbhsBoth + BracketMatchColor.Background = clNone + BracketMatchColor.Foreground = clNone + BracketMatchColor.Style = [fsBold] + FoldedCodeColor.Background = clNone + FoldedCodeColor.Foreground = clGray + FoldedCodeColor.FrameColor = clGray + MouseLinkColor.Background = clNone + MouseLinkColor.Foreground = clBlue + LineHighlightColor.Background = clNone + LineHighlightColor.Foreground = clNone + OnChange = Modification + inline SynLeftGutterPartList1: TSynGutterPartList + object SynGutterMarks1: TSynGutterMarks + Width = 30 + MouseActions = <> + end + object SynGutterLineNumber1: TSynGutterLineNumber + Width = 17 + MouseActions = <> + MarkupInfo.Background = clBtnFace + MarkupInfo.Foreground = clNone + DigitCount = 2 + ShowOnlyLineNumbersMultiplesOf = 1 + ZeroStart = False + LeadingZeros = False + end + object SynGutterChanges1: TSynGutterChanges + Width = 5 + MouseActions = <> + ModifiedColor = 59900 + SavedColor = clGreen + end + object SynGutterSeparator1: TSynGutterSeparator + Width = 3 + MouseActions = <> + MarkupInfo.Background = clWhite + MarkupInfo.Foreground = clGray + end + object SynGutterCodeFolding1: TSynGutterCodeFolding + Width = 13 + MouseActions = <> + MarkupInfo.Background = clNone + MarkupInfo.Foreground = clGray + MouseActionsExpanded = <> + MouseActionsCollapsed = <> + end + end + end + object btnDiscard: TButton + AnchorSideLeft.Control = btnHelp + AnchorSideLeft.Side = asrBottom + AnchorSideBottom.Control = Owner + AnchorSideBottom.Side = asrBottom + Left = 106 + Height = 31 + Top = 588 + Width = 94 + Anchors = [akLeft, akBottom] + BorderSpacing.Around = 6 + Caption = 'Discard' + TabOrder = 3 + OnClick = btnDiscardClick + end + object btnSave: TButton + AnchorSideLeft.Control = btnDiscard + AnchorSideLeft.Side = asrBottom + AnchorSideBottom.Control = Owner + AnchorSideBottom.Side = asrBottom + Left = 206 + Height = 31 + Top = 588 + Width = 94 + Anchors = [akLeft, akBottom] + BorderSpacing.Around = 6 + Caption = 'Save' + Default = True + TabOrder = 4 + OnClick = btnSaveClick + end + object btnHelp: TButton + AnchorSideLeft.Control = Owner + AnchorSideBottom.Control = Owner + AnchorSideBottom.Side = asrBottom + Left = 6 + Height = 31 + Top = 588 + Width = 94 + Anchors = [akLeft, akBottom] + BorderSpacing.Around = 6 + Caption = 'Help' + TabOrder = 2 + OnClick = btnHelpClick + end + object PageControlMain: TPageControl + AnchorSideLeft.Control = Owner + AnchorSideTop.Control = Owner + AnchorSideRight.Control = Owner + AnchorSideRight.Side = asrBottom + Left = 6 + Height = 221 + Top = 6 + Width = 863 + ActivePage = tabOptions + Align = alTop + BorderSpacing.Around = 6 + TabIndex = 0 + TabOrder = 0 + OnChange = PageControlMainChange + object tabOptions: TTabSheet + Caption = 'Options' + ClientHeight = 188 + ClientWidth = 855 + ImageIndex = 14 + object lblName: TLabel + AnchorSideLeft.Control = tabOptions + AnchorSideTop.Control = tabOptions + Left = 6 + Height = 20 + Top = 6 + Width = 43 + BorderSpacing.Around = 6 + Caption = 'Name:' + end + object lblDefiner: TLabel + AnchorSideLeft.Control = editName + AnchorSideLeft.Side = asrBottom + AnchorSideTop.Control = tabOptions + Left = 499 + Height = 20 + Top = 6 + Width = 52 + BorderSpacing.Around = 6 + Caption = 'Definer:' + end + object lblSecurity: TLabel + AnchorSideLeft.Control = rgAlgorithm + AnchorSideLeft.Side = asrBottom + AnchorSideTop.Control = comboDefiner + AnchorSideTop.Side = asrBottom + AnchorSideRight.Control = tabOptions + AnchorSideRight.Side = asrBottom + Left = 501 + Height = 20 + Top = 40 + Width = 83 + BorderSpacing.Around = 6 + Caption = 'SQL security:' + end + object rgAlgorithm: TRadioGroup + AnchorSideLeft.Control = tabOptions + AnchorSideTop.Control = editName + AnchorSideTop.Side = asrBottom + AnchorSideRight.Side = asrBottom + AnchorSideBottom.Control = tabOptions + AnchorSideBottom.Side = asrBottom + Left = 6 + Height = 142 + Top = 40 + Width = 489 + Anchors = [akTop, akLeft, akBottom] + AutoFill = True + BorderSpacing.Around = 6 + Caption = 'Algorithm' + ChildSizing.LeftRightSpacing = 6 + ChildSizing.EnlargeHorizontal = crsHomogenousChildResize + ChildSizing.EnlargeVertical = crsHomogenousChildResize + ChildSizing.ShrinkHorizontal = crsScaleChilds + ChildSizing.ShrinkVertical = crsScaleChilds + ChildSizing.Layout = cclLeftToRightThenTopToBottom + ChildSizing.ControlsPerLine = 1 + ClientHeight = 117 + ClientWidth = 485 + ItemIndex = 0 + Items.Strings = ( + 'UNDEFINED' + 'MERGE' + 'TEMPTABLE' + ) + OnClick = Modification + TabOrder = 3 + end + object editName: TEdit + AnchorSideTop.Control = tabOptions + Left = 105 + Height = 28 + Top = 6 + Width = 388 + BorderSpacing.Around = 6 + TabOrder = 0 + Text = 'editName' + TextHint = 'Enter view name' + OnChange = Modification + end + object comboDefiner: TComboBox + AnchorSideTop.Control = tabOptions + AnchorSideRight.Control = tabOptions + AnchorSideRight.Side = asrBottom + Left = 606 + Height = 28 + Top = 6 + Width = 243 + Anchors = [akTop, akLeft, akRight] + BorderSpacing.Around = 6 + ItemHeight = 20 + TabOrder = 1 + Text = 'comboDefiner' + OnChange = Modification + OnDropDown = comboDefinerDropDown + end + object comboSecurity: TComboBox + AnchorSideRight.Control = tabOptions + AnchorSideRight.Side = asrBottom + Left = 606 + Height = 28 + Top = 38 + Width = 243 + Anchors = [akTop, akLeft, akRight] + BorderSpacing.Around = 6 + ItemHeight = 20 + Style = csDropDownList + TabOrder = 2 + OnChange = Modification + end + object rgCheck: TRadioGroup + AnchorSideLeft.Control = rgAlgorithm + AnchorSideLeft.Side = asrBottom + AnchorSideTop.Control = comboSecurity + AnchorSideTop.Side = asrBottom + AnchorSideRight.Control = tabOptions + AnchorSideRight.Side = asrBottom + AnchorSideBottom.Control = tabOptions + AnchorSideBottom.Side = asrBottom + Left = 501 + Height = 108 + Top = 72 + Width = 348 + Anchors = [akTop, akLeft, akRight] + AutoFill = True + BorderSpacing.Around = 6 + Caption = 'Check option for updates' + ChildSizing.LeftRightSpacing = 6 + ChildSizing.EnlargeHorizontal = crsHomogenousChildResize + ChildSizing.EnlargeVertical = crsHomogenousChildResize + ChildSizing.ShrinkHorizontal = crsScaleChilds + ChildSizing.ShrinkVertical = crsScaleChilds + ChildSizing.Layout = cclLeftToRightThenTopToBottom + ChildSizing.ControlsPerLine = 1 + ClientHeight = 83 + ClientWidth = 344 + ItemIndex = 0 + Items.Strings = ( + 'None' + 'CASCADED' + 'LOCAL' + ) + OnClick = Modification + TabOrder = 4 + end + end + object tabCreateCode: TTabSheet + Caption = 'CREATE code' + ClientHeight = 188 + ClientWidth = 855 + ImageIndex = 39 + inline SynMemoCreateCode: TSynEdit + Left = 0 + Height = 188 + Top = 0 + Width = 855 + Align = alClient + Font.Color = clWindowText + Font.Height = -13 + Font.Name = 'Courier New' + Font.Pitch = fpFixed + Font.Quality = fqNonAntialiased + ParentColor = False + ParentFont = False + TabOrder = 0 + Gutter.Width = 68 + Gutter.MouseActions = <> + RightGutter.Width = 0 + RightGutter.MouseActions = <> + Keystrokes = <> + MouseActions = <> + MouseTextActions = <> + MouseSelActions = <> + VisibleSpecialChars = [vscSpace, vscTabAtLast] + ReadOnly = True + SelectedColor.BackPriority = 50 + SelectedColor.ForePriority = 50 + SelectedColor.FramePriority = 50 + SelectedColor.BoldPriority = 50 + SelectedColor.ItalicPriority = 50 + SelectedColor.UnderlinePriority = 50 + SelectedColor.StrikeOutPriority = 50 + BracketHighlightStyle = sbhsBoth + BracketMatchColor.Background = clNone + BracketMatchColor.Foreground = clNone + BracketMatchColor.Style = [fsBold] + FoldedCodeColor.Background = clNone + FoldedCodeColor.Foreground = clGray + FoldedCodeColor.FrameColor = clGray + MouseLinkColor.Background = clNone + MouseLinkColor.Foreground = clBlue + LineHighlightColor.Background = clNone + LineHighlightColor.Foreground = clNone + inline SynLeftGutterPartList1: TSynGutterPartList + object SynGutterMarks1: TSynGutterMarks + Width = 30 + MouseActions = <> + end + object SynGutterLineNumber1: TSynGutterLineNumber + Width = 17 + MouseActions = <> + MarkupInfo.Background = clBtnFace + MarkupInfo.Foreground = clNone + DigitCount = 2 + ShowOnlyLineNumbersMultiplesOf = 1 + ZeroStart = False + LeadingZeros = False + end + object SynGutterChanges1: TSynGutterChanges + Width = 5 + MouseActions = <> + ModifiedColor = 59900 + SavedColor = clGreen + end + object SynGutterSeparator1: TSynGutterSeparator + Width = 3 + MouseActions = <> + MarkupInfo.Background = clWhite + MarkupInfo.Foreground = clGray + end + object SynGutterCodeFolding1: TSynGutterCodeFolding + Width = 13 + MouseActions = <> + MarkupInfo.Background = clNone + MarkupInfo.Foreground = clGray + MouseActionsExpanded = <> + MouseActionsCollapsed = <> + end + end + end + end + end +end diff --git a/source/view.pas b/source/view.pas new file mode 100644 index 00000000..c83735b6 --- /dev/null +++ b/source/view.pas @@ -0,0 +1,260 @@ +unit view; + +{$mode delphi}{$H+} + +interface + +uses + SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, SynEdit, + ExtCtrls, Menus, LCLProc, + dbconnection, dbstructures, dbstructures.mysql, apphelpers, ComCtrls, extra_controls; + +type + TFrame = TDBObjectEditor; + TfrmView = class(TFrame) + SynMemoBody: TSynEdit; + lblSelect: TLabel; + btnDiscard: TButton; + btnSave: TButton; + btnHelp: TButton; + lblDisabledWhy: TLabel; + PageControlMain: TPageControl; + tabOptions: TTabSheet; + tabCreateCode: TTabSheet; + rgAlgorithm: TRadioGroup; + lblName: TLabel; + editName: TEdit; + lblDefiner: TLabel; + comboDefiner: TComboBox; + comboSecurity: TComboBox; + lblSecurity: TLabel; + rgCheck: TRadioGroup; + SynMemoCreateCode: TSynEdit; + procedure btnHelpClick(Sender: TObject); + procedure btnSaveClick(Sender: TObject); + procedure btnDiscardClick(Sender: TObject); + procedure Modification(Sender: TObject); + procedure comboDefinerDropDown(Sender: TObject); + procedure PageControlMainChange(Sender: TObject); + private + { Private declarations } + function ComposeCreateStatement: TSQLBatch; + public + { Public declarations } + constructor Create(AOwner: TComponent); override; + procedure Init(Obj: TDBObject); override; + function ApplyModifications: TModalResult; override; + end; + + +implementation + +uses main; + +{$R *.lfm} + + +{** + Create: Restore GUI setup +} +constructor TfrmView.Create(AOwner: TComponent); +begin + inherited; + SynMemoBody.Highlighter := Mainform.SynSQLSynUsed; + Mainform.SynCompletionProposal.AddEditor(SynMemoBody); + SynMemoCreateCode.Highlighter := Mainform.SynSQLSynUsed; + Mainform.SynCompletionProposal.AddEditor(SynMemoCreateCode); + editName.MaxLength := NAME_LEN; + comboSecurity.Items.Add('Definer'); + comboSecurity.Items.Add('Invoker'); + FMainSynMemo := SynMemoBody; + btnSave.Hint := ShortCutToText(MainForm.actSaveSQL.ShortCut); +end; + + +{** + FormShow: Fill controls with content in edit mode +} +procedure TfrmView.Init(Obj: TDBObject); +var + Algorithm, CheckOption, SelectCode, Definer, SQLSecurity: String; + i: Integer; +begin + inherited; + lblDisabledWhy.Font.Color := clRed; + comboDefiner.Text := ''; + comboDefiner.TextHint := f_('Current user (%s)', [Obj.Connection.CurrentUserHostCombination]); + comboDefiner.Hint := f_('Leave empty for current user (%s)', [Obj.Connection.CurrentUserHostCombination]); + if Obj.Name <> '' then begin + // Edit mode + editName.Text := Obj.Name; + Obj.Connection.ParseViewStructure(Obj.CreateCode, Obj, Algorithm, Definer, SQLSecurity, CheckOption, SelectCode); + comboDefiner.Text := Definer; + rgAlgorithm.ItemIndex := rgAlgorithm.Items.IndexOf(Algorithm); + rgCheck.ItemIndex := rgCheck.Items.IndexOf(CheckOption); + if rgCheck.ItemIndex = -1 then + rgCheck.ItemIndex := 0; + for i:=0 to comboSecurity.Items.Count-1 do begin + if LowerCase(SQLSecurity) = LowerCase(comboSecurity.Items[i]) then begin + comboSecurity.ItemIndex := i; + Break; + end; + end; + SynMemoBody.Text := SelectCode; + // User may not be allowed to run SHOW CREATE VIEW, in which case we have an empty CreateCode. + // Disable editor in this case. + lblDisabledWhy.Visible := SelectCode = ''; + editName.Enabled := not lblDisabledWhy.Visible; + rgAlgorithm.Enabled := editName.Enabled; + rgCheck.Enabled := rgAlgorithm.Enabled; + SynMemoBody.Enabled := rgAlgorithm.Enabled; + SynMemoBody.TopLine := FMainSynMemoPreviousTopLine; + end else begin + // Create mode + editName.Text := ''; + rgAlgorithm.Enabled := True; + rgAlgorithm.ItemIndex := 0; + rgCheck.Enabled := True; + rgCheck.ItemIndex := 0; + comboSecurity.ItemIndex := 0; + SynMemoBody.Text := 'SELECT '; + lblDisabledWhy.Hide; + end; + + // Most clauses only supported by MySQL + comboDefiner.Enabled := comboDefiner.Enabled and Obj.Connection.Parameters.IsAnyMySQL; + lblDefiner.Enabled := comboDefiner.Enabled; + comboSecurity.Enabled := comboSecurity.Enabled and Obj.Connection.Parameters.IsAnyMySQL; + lblSecurity.Enabled := comboSecurity.Enabled; + rgAlgorithm.Enabled := rgAlgorithm.Enabled and Obj.Connection.Parameters.IsAnyMySQL; + rgCheck.Enabled := rgCheck.Enabled and Obj.Connection.Parameters.IsAnyMySQL; + + // Update create code tab + Modification(Self); + Modified := False; + btnSave.Enabled := Modified; + btnDiscard.Enabled := Modified; + Mainform.ShowStatusMsg; + TExtForm.PageControlTabHighlight(PageControlMain); + Screen.Cursor := crDefault; +end; + + +procedure TfrmView.comboDefinerDropDown(Sender: TObject); +begin + // Populate definers from mysql.user + (Sender as TComboBox).Items.Assign(DBObject.Connection.AllUserHostCombinations); +end; + + +{** + Lookup "Create|Alter View" in SQL help +} +procedure TfrmView.btnHelpClick(Sender: TObject); +begin + Help(Self, 'createview'); +end; + + +procedure TfrmView.btnDiscardClick(Sender: TObject); +begin + // Reinit editor, discarding changes + Modified := False; + Init(DBObject); +end; + + +{** + Apply changes +} +procedure TfrmView.btnSaveClick(Sender: TObject); +begin + ApplyModifications; +end; + + +function TfrmView.ApplyModifications: TModalResult; +var + Batch: TSQLBatch; + Query: TSQLSentence; +begin + // Save changes + Result := mrOk; + + try + Batch := ComposeCreateStatement; + for Query in Batch do begin + DBObject.Connection.Query(Query.SQL); + end; + Batch.Free; + DBObject.Name := editName.Text; + DBObject.UnloadDetails; + Mainform.UpdateEditorTab; + Mainform.RefreshTree(DBObject); + Modified := False; + btnSave.Enabled := Modified; + btnDiscard.Enabled := Modified; + except + on E:EDbError do begin + ErrorDialog(E.Message); + Result := mrAbort; + end; + end; +end; + + +procedure TfrmView.Modification(Sender: TObject); +var + Batch: TSQLBatch; +begin + Modified := True; + btnSave.Enabled := Modified and (editName.Text <> ''); + btnDiscard.Enabled := Modified; + // Update create code + Batch := ComposeCreateStatement; + SynMemoCreateCode.Text := Batch.SQL; + Batch.Free; +end; + + +procedure TfrmView.PageControlMainChange(Sender: TObject); +begin + TExtForm.PageControlTabHighlight(PageControlMain); +end; + + +function TfrmView.ComposeCreateStatement: TSQLBatch; +var + sql, ViewName, RenameView: String; +begin + // Create or Alter code + if not ObjectExists then begin + sql := 'CREATE '; + ViewName := editName.Text; + end else begin + sql := 'ALTER '; + ViewName := DBObject.Name; + end; + ViewName := DBObject.Connection.QuoteIdent(ViewName); + if rgAlgorithm.Enabled and (rgAlgorithm.ItemIndex > -1) then + sql := sql + 'ALGORITHM = '+Uppercase(rgAlgorithm.Items[rgAlgorithm.ItemIndex])+' '; + if comboDefiner.Enabled and (comboDefiner.Text <> '') then + sql := sql + 'DEFINER='+DBObject.Connection.QuoteIdent(comboDefiner.Text, True, '@')+' '; + if comboSecurity.Enabled and (comboSecurity.Text <> '') then + sql := sql + 'SQL SECURITY ' + UpperCase(comboSecurity.Text)+' '; + sql := sql + 'VIEW ' + ViewName+' AS '+SynMemoBody.Text+' '; + if rgCheck.Enabled and (rgCheck.ItemIndex > 0) then + sql := sql + 'WITH '+Uppercase(rgCheck.Items[rgCheck.ItemIndex])+' CHECK OPTION'; + sql := sql + ';' + sLineBreak; + + if ObjectExists and (DBObject.Name <> editName.Text) then begin + RenameView := DBObject.Connection.QuoteIdent(editName.Text); + sql := sql + 'RENAME TABLE '+ViewName + ' TO '+RenameView + ';' + sLineBreak; + end; + + Result := TSQLBatch.Create; + Result.SQL := Trim(SQL); +end; + + +end.