diff --git a/source/dbconnection.pas b/source/dbconnection.pas index fffd0417..f8fc2392 100644 --- a/source/dbconnection.pas +++ b/source/dbconnection.pas @@ -332,7 +332,7 @@ type procedure ParseTableStructure(CreateTable: String; Columns: TTableColumnList; Keys: TTableKeyList; ForeignKeys: TForeignKeyList); procedure ParseViewStructure(CreateCode, ViewName: String; Columns: TTableColumnList; var Algorithm, Definer, SQLSecurity, CheckOption, SelectCode: String); - procedure ParseRoutineStructure(CreateCode: String; Parameters: TRoutineParamList; + procedure ParseRoutineStructure(Obj: TDBObject; Parameters: TRoutineParamList; var Deterministic: Boolean; var Definer, Returns, DataAccess, Security, Comment, Body: String); function GetDatatypeByName(Datatype: String): TDBDatatype; function ApplyLimitClause(QueryType, QueryBody: String; Limit, Offset: Cardinal): String; @@ -3422,10 +3422,10 @@ begin end; -procedure TDBConnection.ParseRoutineStructure(CreateCode: String; Parameters: TRoutineParamList; +procedure TDBConnection.ParseRoutineStructure(Obj: TDBObject; Parameters: TRoutineParamList; var Deterministic: Boolean; var Definer, Returns, DataAccess, Security, Comment, Body: String); var - Params: String; + CreateCode, Params: String; ParenthesesCount: Integer; rx: TRegExpr; i: Integer; @@ -3439,6 +3439,7 @@ begin // CREATE DEFINER=`root`@`localhost` FUNCTION `test3`(`?b` varchar(20)) RETURNS tinyint(4) // CREATE DEFINER=`root`@`localhost` PROCEDURE `test3`(IN `Param1` int(1) unsigned) + CreateCode := Obj.CreateCode; rx.Expression := '\bDEFINER\s*=\s*(\S+)\s'; if rx.Exec(CreateCode) then Definer := DequoteIdent(rx.Match[1], '@') @@ -3482,12 +3483,18 @@ begin // | SQL SECURITY { DEFINER | INVOKER } // SECURITY_TYPE // | COMMENT 'string' // COMMENT - // Strip body early, so regular expressions don't get confused by keywords - rx.Expression := '\bBEGIN\b'; - if rx.Exec(CreateCode) then begin - Body := TrimLeft(Copy(CreateCode, rx.MatchPos[0], MaxInt)); - Delete(CreateCode, rx.MatchPos[0], MaxInt); - end; + // Get the body from information_schema, as it's impossible to detect its start position in the CREATE code + // See http://www.heidisql.com/forum.php?t=12075 + Body := GetVar('SELECT '+QuoteIdent('ROUTINE_DEFINITION')+ + ' FROM information_schema.'+QuoteIdent('ROUTINES')+ + ' WHERE '+QuoteIdent('ROUTINE_SCHEMA')+'='+EscapeString(Obj.Database)+ + ' AND '+QuoteIdent('ROUTINE_NAME')+'='+EscapeString(Obj.Name)+ + ' AND '+QuoteIdent('ROUTINE_TYPE')+'='+EscapeString(UpperCase(Obj.ObjType)) + ); + // Strip body early, so regular expressions don't get confused by keywords, + // e.g. "RETURNS" can be inside the body + Delete(CreateCode, Length(CreateCode)-Length(Body), MaxInt); + rx.Expression := '\bLANGUAGE SQL\b'; if rx.Exec(CreateCode) then Delete(CreateCode, rx.MatchPos[0], rx.MatchLen[0]); diff --git a/source/main.pas b/source/main.pas index e686d8bf..361034f4 100644 --- a/source/main.pas +++ b/source/main.pas @@ -3320,7 +3320,7 @@ begin Query := 'EXEC '; end; Parameters := TRoutineParamList.Create; - Obj.Connection.ParseRoutineStructure(Obj.CreateCode, Parameters, DummyBool, DummyStr, DummyStr, DummyStr, DummyStr, DummyStr, DummyStr); + Obj.Connection.ParseRoutineStructure(Obj, Parameters, DummyBool, DummyStr, DummyStr, DummyStr, DummyStr, DummyStr, DummyStr); Query := Query + Obj.QuotedName; Params := TStringList.Create; for Param in Parameters do begin @@ -5088,7 +5088,7 @@ begin for DbObj in DbObjects do begin if (CompareText(DbObj.Name, Identifier)=0) and (DbObj.NodeType in [lntFunction, lntProcedure]) then begin Params := TRoutineParamList.Create(True); - DbObj.Connection.ParseRoutineStructure(DbObj.CreateCode, Params, DummyBool, DummyStr, DummyStr, DummyStr, DummyStr, DummyStr, DummyStr); + DbObj.Connection.ParseRoutineStructure(DbObj, Params, DummyBool, DummyStr, DummyStr, DummyStr, DummyStr, DummyStr, DummyStr); ItemText := ''; for i:=0 to Params.Count-1 do ItemText := ItemText + '"' + Params[i].Name + ': ' + Params[i].Datatype + '", '; diff --git a/source/routine_editor.pas b/source/routine_editor.pas index d664f685..5f3ee86f 100644 --- a/source/routine_editor.pas +++ b/source/routine_editor.pas @@ -162,7 +162,7 @@ begin lntProcedure: comboType.ItemIndex := 0; lntFunction: comboType.ItemIndex := 1; end; - DBObject.Connection.ParseRoutineStructure(Obj.CreateCode, Parameters, Deterministic, Definer, Returns, DataAccess, Security, Comment, Body); + DBObject.Connection.ParseRoutineStructure(Obj, Parameters, Deterministic, Definer, Returns, DataAccess, Security, Comment, Body); comboReturns.Text := Returns; chkDeterministic.Checked := Deterministic; if DataAccess <> '' then @@ -173,16 +173,6 @@ begin comboDefiner.Text := Definer; // The whole CREATE CODE may be empty if the user is not allowed to view code in SHOW CREATE FUNCTION // => Disable the whole editor in this case. - // Also, only the body may be empty, because ParseRoutineBody did not get a BEGIN..END block to see. - // See http://www.heidisql.com/forum.php?t=12075 - // => Get the body from information_schema - if Body = '' then begin - Body := Obj.Connection.GetVar('SELECT '+Obj.Connection.QuoteIdent('ROUTINE_DEFINITION')+' FROM information_schema.'+Obj.Connection.QuoteIdent('ROUTINES')+ - ' WHERE '+Obj.Connection.QuoteIdent('ROUTINE_SCHEMA')+'='+esc(Obj.Database)+ - ' AND '+Obj.Connection.QuoteIdent('ROUTINE_NAME')+'='+esc(Obj.Name)+ - ' AND '+Obj.Connection.QuoteIdent('ROUTINE_TYPE')+'='+esc(FAlterRoutineType) - ); - end; SynMemoBody.Text := Body; lblDisabledWhy.Visible := Body = ''; PageControlMain.Enabled := not lblDisabledWhy.Visible;