From 1d3bfe6219593e95dff8b17483ba2db24f0b55dd Mon Sep 17 00:00:00 2001 From: Ansgar Becker Date: Sun, 6 Jul 2008 20:22:16 +0000 Subject: [PATCH] Fix and simplify delimiter customizing, now that the dropdown was moved to the mainform and didn't fire the OnExit event any longer for some reason. The new mechanism just uses a button (surely including a new icon, sigh...) which asks via InputQuery for the new value. Delimmiter "validation" is only done when setting the value per button, not in parseSql() any longer - avoids being bug-per-bug compatible with MySQL's SQL parser at least in the non interactive mode. (Hope that code is ok with Franciscos original implementation. At least the old validation IF's are adapted) --- components/heidisql/include/const.inc | 7 +- res/icons/delimiter.png | Bin 0 -> 266 bytes source/childwin.pas | 40 +------- source/helpers.pas | 74 +-------------- source/main.dfm | 59 +++++------- source/main.pas | 127 +++++++++++--------------- 6 files changed, 89 insertions(+), 218 deletions(-) create mode 100644 res/icons/delimiter.png diff --git a/components/heidisql/include/const.inc b/components/heidisql/include/const.inc index 718abf4d..12ac870e 100644 --- a/components/heidisql/include/const.inc +++ b/components/heidisql/include/const.inc @@ -103,8 +103,8 @@ const REGNAME_SQLOUTHEIGHT = 'sqloutheight'; REGNAME_QUERYHELPERSWIDTH = 'queryhelperswidth'; REGNAME_SQLWHEREFILE = 'SQLWhereFile'; - REGNAME_DELIMITERS = 'delimiters'; - REGNAME_DELIMITERSELECTED = 'delimiterselected'; + REGNAME_DELIMITER = 'Delimiter'; + DEFAULT_DELIMITER = ';'; REGNAME_SQLHELPWINLEFT = 'SQLHelp_WindowLeft'; REGNAME_SQLHELPWINTOP = 'SQLHelp_WindowTop'; REGNAME_SQLHELPWINWIDTH = 'SQLHelp_WindowWidth'; @@ -209,9 +209,6 @@ const {TeraByte} NAME_TB = ' TB'; {PetaByte} NAME_PB = ' PB'; - // See reference: mysql.cpp Ver 14.12 Distrib 5.0.45, for Win32 (ia32): Line 112 - DEFAULT_DELIMITER = ';'; - // Copied constants from [delphi11]\source\win32\rtl\win\ShlObj.pas to make them // available in Delphi 10. We don't use the constants from ShlObj until delphi 10 // support is removed. diff --git a/res/icons/delimiter.png b/res/icons/delimiter.png new file mode 100644 index 0000000000000000000000000000000000000000..c8c0a1f56cc990b3465dbb531ed16174aa713b59 GIT binary patch literal 266 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCijSl0AZa85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP(q@_ zHKHUqKdq!Zu_%=xATcwqM9 0 then result := 'Backslash disallowed in DELIMITER (because the MySQL CLI does not accept it)'; - // Disallow stuff which would be negated by the comment parsing logic. - if - (Pos('/*', s) > 0) or - (Pos('--', s) > 0) or - (Pos('#', s) > 0) - then result := 'Start-of-comment tokens disallowed in DELIMITER (because it would be ignored)'; - // Disallow stuff which would be negated by the SQL parser (and could slightly confuse it, if at end-of-string). - if - (Pos('''', s) > 0) or - (Pos('`', s) > 0) or - (Pos('"', s) > 0) - then result := 'String literal markers disallowed in DELIMITER (because it would be ignored)'; - - if result <> '' then begin - result := WideFormat('Invalid delimiter %s: %s.', [s, result]); - end; -end; - - - {*** Return true if given character represents whitespace. Limitations: only recognizes ANSI whitespace. @@ -459,10 +422,9 @@ end; @param String (possibly large) bunch of SQL-statements, separated by semicolon @param String SQL start delimiter - @param TParseSQLProcessCommand Method that execute actions relative to an object @return TStringList Separated statements } -function parsesql(sql: WideString; delimiter: WideString; processcommand: TParseSQLProcessCommand = nil) : TWideStringList; +function parsesql(sql: WideString; delimiter: WideString) : TWideStringList; var i, j, start, len : Integer; tmp : WideString; @@ -472,26 +434,6 @@ var delimiter_length : Integer; encloser, secchar, thdchar : WideChar; conditional : WideString; - msg : WideString; - -{*** - If a callback for processing client SQL etc was given, invoke it. -} -procedure CallProcessCommand(command: WideString; parameter: WideString); -begin - if Assigned(processcommand) then processcommand(command, parameter); -end; - -{*** - Updates the delimiter in the GUI from the one specified via pseudo-SQL. -} -procedure UpdateDelimiterData(execute_callback: Boolean = true); -begin - if (execute_callback) then CallProcessCommand('DELIMITER', delimiter); - // update the delimiter variables helper - delimiter_length := Length(delimiter); -end; - begin result := TWideStringList.Create; sql := trim(sql); @@ -507,8 +449,6 @@ begin encloser := ' '; conditional := ''; - UpdateDelimiterData(false); - i := 0; while i < len do begin i := i + 1; @@ -589,14 +529,7 @@ begin if indelimiter then begin if (sql[i] in [WideChar(#13), WideChar(#10)]) or (i = len) then begin if (i = len) then j := 1 else j := 0; - tmp := copy(sql, start + 10, i + j - (start + 10)); - msg := IsValidDelimiter(tmp); - if msg = '' then begin - delimiter := tmp; - UpdateDelimiterData(true); - end else begin - CallProcessCommand('CLIENTSQL_ERROR', msg); - end; + delimiter := copy(sql, start + 10, i + j - (start + 10)); indelimiter := false; start := i + 1; end; @@ -648,6 +581,7 @@ begin end; // Add sql sentence. + delimiter_length := Length(delimiter); if ((not instring) and (scanReverse(sql, i, delimiter, delimiter_length, false)) or (i = len)) then begin if (i < len) then j := delimiter_length else begin // end of string, add sql sentence but only remove delimiter if it's there diff --git a/source/main.dfm b/source/main.dfm index 29bb84b2..f27ade88 100644 --- a/source/main.dfm +++ b/source/main.dfm @@ -401,7 +401,7 @@ object MainForm: TMainForm object ToolBarQuery: TToolBar Left = 398 Top = 28 - Width = 353 + Width = 266 Height = 22 Align = alNone AutoSize = True @@ -463,42 +463,10 @@ object MainForm: TMainForm Top = 0 Action = actQueryWordWrap end - object Panel1: TPanel + object btnSetDelimiter: TToolButton Left = 243 Top = 0 - Width = 110 - Height = 22 - BevelOuter = bvNone - UseDockManager = False - ParentBackground = False - TabOrder = 0 - object LabelQueryDelimiter: TLabel - Left = 6 - Top = 4 - Width = 45 - Height = 13 - Caption = 'Delimiter:' - Font.Charset = DEFAULT_CHARSET - Font.Color = clBlack - Font.Height = -11 - Font.Name = 'Tahoma' - Font.Style = [] - ParentFont = False - end - object ComboBoxQueryDelimiter: TComboBox - Left = 55 - Top = 0 - Width = 45 - Height = 21 - Enabled = False - ItemHeight = 13 - TabOrder = 0 - OnExit = ComboBoxQueryDelimiterExit - Items.Strings = ( - ';' - ';;' - '//') - end + Action = actSetDelimiter end end end @@ -1279,6 +1247,14 @@ object MainForm: TMainForm ShortCut = 16466 OnExecute = actQueryReplaceExecute end + object actSetDelimiter: TAction + Category = 'SQL' + Caption = 'Set delimiter used in SQL execution' + Enabled = False + Hint = 'Set delimiter used in SQL execution' + ImageIndex = 106 + OnExecute = actSetDelimiterExecute + end end object SaveDialog2: TSaveDialog DefaultExt = 'reg' @@ -4413,6 +4389,19 @@ object MainForm: TMainForm 44AE426082} Name = 'PngImage105' Background = clWindow + end + item + PngImage.Data = { + 89504E470D0A1A0A0000000D49484452000000100000001008060000001FF3FF + 61000000017352474200AECE1CE90000001874455874536F6674776172650050 + 61696E742E4E45542076332E313072B225920000007D4944415478DA6364A010 + 30E2927861CE700B89BB40E224431B3906A842B9D50362401512F718D0800324 + 19407120526C00D00BEB80942894BB02E885A9A41A30F0B100F2820ED4901C92 + BD0035640E904A066213A00167C931009C9C819AD570A9C1E785C3404A1C884B + 80066C22C700504A9C0FD4FC1C9F2B294E480072902F11D735460A0000000049 + 454E44AE426082} + Name = 'PngImage106' + Background = clWindow end> PngOptions = [pngBlendOnDisabled, pngGrayscaleOnDisabled] Left = 8 diff --git a/source/main.pas b/source/main.pas index afa56a89..95a770a5 100644 --- a/source/main.pas +++ b/source/main.pas @@ -218,12 +218,11 @@ type btnQueryReplace: TToolButton; btnStopOnErrors: TToolButton; btnQueryWordwrap: TToolButton; - Panel1: TPanel; - ComboBoxQueryDelimiter: TComboBox; - LabelQueryDelimiter: TLabel; PopupQueryLoad: TPopupMenu; btnEditView: TToolButton; btnExecuteLine: TToolButton; + actSetDelimiter: TAction; + btnSetDelimiter: TToolButton; procedure actCreateFieldExecute(Sender: TObject); procedure actEditTablePropertiesExecute(Sender: TObject); procedure actCreateTableExecute(Sender: TObject); @@ -284,10 +283,10 @@ type procedure actRefreshExecute(Sender: TObject); procedure actSaveSQLExecute(Sender: TObject); procedure actSaveSQLSnippetExecute(Sender: TObject); + procedure actSetDelimiterExecute(Sender: TObject); procedure actSQLhelpExecute(Sender: TObject); procedure actUpdateCheckExecute(Sender: TObject); procedure actWebbrowse(Sender: TObject); - procedure ComboBoxQueryDelimiterExit(Sender: TObject); procedure EnsureConnected; function ExecuteRemoteQuery(sender: THandle; query: string): TDataSet; procedure ExecuteRemoteNonQuery(sender: THandle; query: string); @@ -302,6 +301,7 @@ type function GetChildwin: TMDIChild; function GetParamValue(const paramChar: Char; const paramName: string; var curIdx: Byte; out paramValue: string): Boolean; + procedure UpdateDelimiterHint; public MaintenanceForm: TOptimize; ViewForm: TfrmView; @@ -316,7 +316,6 @@ type procedure popupQueryLoadClick( sender: TObject ); procedure FillPopupQueryLoad; procedure PopupQueryLoadRemoveAbsentFiles( sender: TObject ); - procedure ComboBoxQueryDelimiterAdd( delimiter: WideString ); function GetRegValue( valueName: String; defaultValue: Integer; Session: String = '' ) : Integer; Overload; function GetRegValue( valueName: String; defaultValue: Boolean; Session: String = '' ) : Boolean; Overload; function GetRegValue( valueName: String; defaultValue: String; Session: String = '' ) : String; Overload; @@ -487,9 +486,8 @@ begin WriteInteger(REGNAME_TOOLBARQUERYLEFT, ToolBarQuery.Left); WriteInteger(REGNAME_TOOLBARQUERYTOP, ToolBarQuery.Top); - // Save the delimiters - WriteString( REGNAME_DELIMITERS, ComboBoxQueryDelimiter.Items.Text ); - WriteInteger( REGNAME_DELIMITERSELECTED, ComboBoxQueryDelimiter.ItemIndex ); + // Save delimiter + WriteString( REGNAME_DELIMITER, Delimiter ); end; CloseKey; Free; @@ -523,7 +521,6 @@ procedure TMainForm.FormCreate(Sender: TObject); var ws : String; Monitor: TMonitor; - delimiters: String; const MoveWinThreshold: Byte = 80; begin @@ -563,14 +560,9 @@ begin ToolBarQuery.Left := GetRegValue(REGNAME_TOOLBARQUERYLEFT, ToolBarQuery.Left); ToolBarQuery.Top := GetRegValue(REGNAME_TOOLBARQUERYTOP, ToolBarQuery.Top); - // Delimiter stuff - delimiters := Trim( Mainform.GetRegValue(REGNAME_DELIMITERS, '') ); - if delimiters <> '' then begin - ComboBoxQueryDelimiter.Items.Text := delimiters; - ComboBoxQueryDelimiter.ItemIndex := GetRegValue( REGNAME_DELIMITERSELECTED, 0 ); - end else - ComboBoxQueryDelimiter.ItemIndex := ComboBoxQueryDelimiter.Items.IndexOf( DEFAULT_DELIMITER ); - Delimiter := ComboBoxQueryDelimiter.Text; + // Delimiter + Delimiter := GetRegValue(REGNAME_DELIMITER, DEFAULT_DELIMITER); + UpdateDelimiterHint; // Beautify AppRevision if Pos('$Rev: WC', AppRevision) < 1 then @@ -1816,61 +1808,6 @@ begin Childwin.SynMemoQuery.WordWrap := TAction(Sender).Checked; end; -procedure TMainForm.ComboBoxQueryDelimiterExit(Sender: TObject); -begin - // a delimiter couldn't be empty - ComboBoxQueryDelimiter.Text := Trim(ComboBoxQueryDelimiter.Text); - // verify if the delimiter combobox isn't empty - if ComboBoxQueryDelimiter.Text = '' then begin - MessageDlg( 'A delimiter is needed.', mtWarning, [mbOK], 0); - ComboBoxQueryDelimiter.SetFocus; - end else begin - // add the new delimiter to combobox - ComboBoxQueryDelimiterAdd(ComboBoxQueryDelimiter.Text); - end; -end; - - -{*** - Add a new query delimiter and select it - @param term The delimiter to add and/or select -} -procedure TMainform.ComboBoxQueryDelimiterAdd( delimiter: WideString ); -var - index: Integer; - found: Boolean; - msg: String; -begin - // See reference: mysql.cpp Ver 14.12 Distrib 5.0.45, for Win32 (ia32): Line 824 - // Check that delimiter does not contain a backslash - msg := IsValidDelimiter( delimiter ); - if msg <> '' then begin - // rollback the delimiter - ComboBoxQueryDelimiter.Text := Delimiter; - // notify the user - raise Exception.Create( msg ); - end else begin - // the delimiter is case-sensitive, following the implementation - // in the MySQL CLI, so we must locate it by hand - found := False; - for index := 0 to ComboBoxQueryDelimiter.Items.Count - 1 do begin - if ComboBoxQueryDelimiter.Items[index] = Delimiter then begin - ComboBoxQueryDelimiter.ItemIndex := index; - found := True; - break; - end; - end; - - if not found then begin - ComboBoxQueryDelimiter.Items.Add( Delimiter ); - ComboBoxQueryDelimiter.ItemIndex := ComboBoxQueryDelimiter.Items.Count - 1; - end; - - Delimiter := ComboBoxQueryDelimiter.Text; - Childwin.LogSQL( Format( 'Delimiter changed to %s.', [Delimiter] )); - end; -end; - procedure TMainForm.FindDialogQueryFind(Sender: TObject); var @@ -2055,4 +1992,50 @@ begin end; +{** + Change default delimiter for SQL execution +} +procedure TMainForm.actSetDelimiterExecute(Sender: TObject); +var + newVal: String; + ok: Boolean; +begin + // Use a while loop to redisplay the input dialog after setting an invalid value + ok := False; + while not ok do begin + newVal := delimiter; + if InputQuery('Set delimiter', 'Delimiter used within SQL execution:', newVal) then begin + // Validate new value + newVal := Trim(newVal); + if (newVal = '') + or (Pos('\', newVal) > 0) + or (Pos('/*', newVal) > 0) + or (Pos('--', newVal) > 0) + or (Pos('#', newVal) > 0) + or (Pos('''', newVal) > 0) + or (Pos('`', newVal) > 0) + or (Pos('"', newVal) > 0) + then begin + MessageDlg('Invalid value: The delimiter must not be empty or contain comment or quoting characters.', + mtError, [mbOK], 0); + end else begin + Delimiter := newVal; + UpdateDelimiterHint; + ok := True; + end; + end else // Cancel clicked + ok := True; + end; +end; + + +{** + Sets the hint of the SetDelimiter TAction so it includes the delimiter itself + and the user has just to move the mouse over it to see it. +} +procedure TMainForm.UpdateDelimiterHint; +begin + actSetDelimiter.Hint := actSetDelimiter.Caption + ' (current value: '+delimiter+')'; +end; + end.