Finally fix ramshackle detection of routine body. Fixes issue #3103, #3104.

This commit is contained in:
Ansgar Becker
2013-02-08 11:25:12 +00:00
parent 28409cf78b
commit 95ab784008
3 changed files with 19 additions and 22 deletions

View File

@ -332,7 +332,7 @@ type
procedure ParseTableStructure(CreateTable: String; Columns: TTableColumnList; Keys: TTableKeyList; ForeignKeys: TForeignKeyList); procedure ParseTableStructure(CreateTable: String; Columns: TTableColumnList; Keys: TTableKeyList; ForeignKeys: TForeignKeyList);
procedure ParseViewStructure(CreateCode, ViewName: String; Columns: TTableColumnList; procedure ParseViewStructure(CreateCode, ViewName: String; Columns: TTableColumnList;
var Algorithm, Definer, SQLSecurity, CheckOption, SelectCode: String); 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); var Deterministic: Boolean; var Definer, Returns, DataAccess, Security, Comment, Body: String);
function GetDatatypeByName(Datatype: String): TDBDatatype; function GetDatatypeByName(Datatype: String): TDBDatatype;
function ApplyLimitClause(QueryType, QueryBody: String; Limit, Offset: Cardinal): String; function ApplyLimitClause(QueryType, QueryBody: String; Limit, Offset: Cardinal): String;
@ -3422,10 +3422,10 @@ begin
end; 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 Deterministic: Boolean; var Definer, Returns, DataAccess, Security, Comment, Body: String);
var var
Params: String; CreateCode, Params: String;
ParenthesesCount: Integer; ParenthesesCount: Integer;
rx: TRegExpr; rx: TRegExpr;
i: Integer; i: Integer;
@ -3439,6 +3439,7 @@ begin
// CREATE DEFINER=`root`@`localhost` FUNCTION `test3`(`?b` varchar(20)) RETURNS tinyint(4) // CREATE DEFINER=`root`@`localhost` FUNCTION `test3`(`?b` varchar(20)) RETURNS tinyint(4)
// CREATE DEFINER=`root`@`localhost` PROCEDURE `test3`(IN `Param1` int(1) unsigned) // CREATE DEFINER=`root`@`localhost` PROCEDURE `test3`(IN `Param1` int(1) unsigned)
CreateCode := Obj.CreateCode;
rx.Expression := '\bDEFINER\s*=\s*(\S+)\s'; rx.Expression := '\bDEFINER\s*=\s*(\S+)\s';
if rx.Exec(CreateCode) then if rx.Exec(CreateCode) then
Definer := DequoteIdent(rx.Match[1], '@') Definer := DequoteIdent(rx.Match[1], '@')
@ -3482,12 +3483,18 @@ begin
// | SQL SECURITY { DEFINER | INVOKER } // SECURITY_TYPE // | SQL SECURITY { DEFINER | INVOKER } // SECURITY_TYPE
// | COMMENT 'string' // COMMENT // | COMMENT 'string' // COMMENT
// Strip body early, so regular expressions don't get confused by keywords // Get the body from information_schema, as it's impossible to detect its start position in the CREATE code
rx.Expression := '\bBEGIN\b'; // See http://www.heidisql.com/forum.php?t=12075
if rx.Exec(CreateCode) then begin Body := GetVar('SELECT '+QuoteIdent('ROUTINE_DEFINITION')+
Body := TrimLeft(Copy(CreateCode, rx.MatchPos[0], MaxInt)); ' FROM information_schema.'+QuoteIdent('ROUTINES')+
Delete(CreateCode, rx.MatchPos[0], MaxInt); ' WHERE '+QuoteIdent('ROUTINE_SCHEMA')+'='+EscapeString(Obj.Database)+
end; ' 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'; rx.Expression := '\bLANGUAGE SQL\b';
if rx.Exec(CreateCode) then if rx.Exec(CreateCode) then
Delete(CreateCode, rx.MatchPos[0], rx.MatchLen[0]); Delete(CreateCode, rx.MatchPos[0], rx.MatchLen[0]);

View File

@ -3320,7 +3320,7 @@ begin
Query := 'EXEC '; Query := 'EXEC ';
end; end;
Parameters := TRoutineParamList.Create; 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; Query := Query + Obj.QuotedName;
Params := TStringList.Create; Params := TStringList.Create;
for Param in Parameters do begin for Param in Parameters do begin
@ -5088,7 +5088,7 @@ begin
for DbObj in DbObjects do begin for DbObj in DbObjects do begin
if (CompareText(DbObj.Name, Identifier)=0) and (DbObj.NodeType in [lntFunction, lntProcedure]) then begin if (CompareText(DbObj.Name, Identifier)=0) and (DbObj.NodeType in [lntFunction, lntProcedure]) then begin
Params := TRoutineParamList.Create(True); 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 := ''; ItemText := '';
for i:=0 to Params.Count-1 do for i:=0 to Params.Count-1 do
ItemText := ItemText + '"' + Params[i].Name + ': ' + Params[i].Datatype + '", '; ItemText := ItemText + '"' + Params[i].Name + ': ' + Params[i].Datatype + '", ';

View File

@ -162,7 +162,7 @@ begin
lntProcedure: comboType.ItemIndex := 0; lntProcedure: comboType.ItemIndex := 0;
lntFunction: comboType.ItemIndex := 1; lntFunction: comboType.ItemIndex := 1;
end; 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; comboReturns.Text := Returns;
chkDeterministic.Checked := Deterministic; chkDeterministic.Checked := Deterministic;
if DataAccess <> '' then if DataAccess <> '' then
@ -173,16 +173,6 @@ begin
comboDefiner.Text := Definer; comboDefiner.Text := Definer;
// The whole CREATE CODE may be empty if the user is not allowed to view code in SHOW CREATE FUNCTION // 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. // => 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; SynMemoBody.Text := Body;
lblDisabledWhy.Visible := Body = ''; lblDisabledWhy.Visible := Body = '';
PageControlMain.Enabled := not lblDisabledWhy.Visible; PageControlMain.Enabled := not lblDisabledWhy.Visible;