Add button and global action for executing selected functions and/or procedures by click. Fixes issue #1818. This requires some parsing code to be moved to helpers unit so it's also available in ListTables' context menu. Also, to avoid new AVs, any db object editor now focuses the edited object in the tree, which is important for creating new ones which were neither existant nor focused.

This commit is contained in:
Ansgar Becker
2010-04-04 22:33:12 +00:00
parent c0533a2f78
commit 45ba97b13f
8 changed files with 253 additions and 128 deletions

View File

@ -10,7 +10,7 @@ interface
uses
Classes, SysUtils, Graphics, GraphUtil, ClipBrd, Dialogs, Forms, Controls, ComCtrls, ShellApi, CheckLst,
Windows, Contnrs, ShlObj, ActiveX, VirtualTrees, SynRegExpr, Messages,
Windows, Contnrs, ShlObj, ActiveX, VirtualTrees, SynRegExpr, Messages, WideStrUtils,
Registry, SynEditHighlighter, DateUtils, Generics.Collections, StrUtils, AnsiStrings, TlHelp32, Types,
mysql_connection, mysql_structures;
@ -132,6 +132,11 @@ type
end;
TForeignKeyList = TObjectList<TForeignKey>;
TRoutineParam = class(TObject)
Name, Context, Datatype: String;
end;
TRoutineParamList = TObjectList<TRoutineParam>;
TDBObjectEditor = class(TFrame)
private
FModified: Boolean;
@ -241,6 +246,8 @@ type
function GetLightness(AColor: TColor): Byte;
procedure ParseTableStructure(CreateTable: String; Columns: TTableColumnList; Keys: TTableKeyList; ForeignKeys: TForeignKeyList);
procedure ParseViewStructure(ViewName: String; Columns: TTableColumnList);
procedure ParseRoutineStructure(CreateCode: String; Parameters: TRoutineParamList;
var Deterministic: Boolean; var Returns, DataAccess, Security, Comment, Body: String);
function ReformatSQL(SQL: String): String;
function ParamBlobToStr(lpData: Pointer): TStringlist;
function ParamStrToBlob(out cbData: DWORD): Pointer;
@ -3031,6 +3038,97 @@ begin
end;
procedure ParseRoutineStructure(CreateCode: String; Parameters: TRoutineParamList;
var Deterministic: Boolean; var Returns, DataAccess, Security, Comment, Body: String);
var
Params: String;
ParenthesesCount: Integer;
rx: TRegExpr;
i: Integer;
Param: TRoutineParam;
begin
// Parse CREATE code of stored function or procedure to detect parameters
rx := TRegExpr.Create;
rx.ModifierI := True;
rx.ModifierG := True;
// CREATE DEFINER=`root`@`localhost` PROCEDURE `bla2`(IN p1 INT, p2 VARCHAR(20))
// CREATE DEFINER=`root`@`localhost` FUNCTION `test3`(`?b` varchar(20)) RETURNS tinyint(4)
// CREATE DEFINER=`root`@`localhost` PROCEDURE `test3`(IN `Param1` int(1) unsigned)
ParenthesesCount := 0;
Params := '';
for i:=1 to Length(CreateCode) do begin
if CreateCode[i] = ')' then begin
Dec(ParenthesesCount);
if ParenthesesCount = 0 then
break;
end;
if ParenthesesCount >= 1 then
Params := Params + CreateCode[i];
if CreateCode[i] = '(' then
Inc(ParenthesesCount);
end;
rx.Expression := '(^|,)\s*((IN|OUT|INOUT)\s+)?(\S+)\s+([^\s,\(]+(\([^\)]*\))?[^,]*)';
if rx.Exec(Params) then while true do begin
Param := TRoutineParam.Create;
Param.Context := UpperCase(rx.Match[3]);
if Param.Context = '' then
Param.Context := 'IN';
Param.Name := WideDequotedStr(rx.Match[4], '`');
Param.Datatype := rx.Match[5];
Parameters.Add(Param);
if not rx.ExecNext then
break;
end;
// Cut left part including parameters, so it's easier to parse the rest
CreateCode := Copy(CreateCode, i+1, MaxInt);
// CREATE PROCEDURE sp_name ([proc_parameter[,...]]) [characteristic ...] routine_body
// CREATE FUNCTION sp_name ([func_parameter[,...]]) RETURNS type [characteristic ...] routine_body
// LANGUAGE SQL
// | [NOT] DETERMINISTIC // IS_DETERMINISTIC
// | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA } // DATA_ACCESS
// | SQL SECURITY { DEFINER | INVOKER } // SECURITY_TYPE
// | COMMENT 'string' // COMMENT
rx.Expression := '\bLANGUAGE SQL\b';
if rx.Exec(CreateCode) then
Delete(CreateCode, rx.MatchPos[0], rx.MatchLen[0]);
rx.Expression := '\bRETURNS\s+(\w+(\([^\)]*\))?)';
if rx.Exec(CreateCode) then begin
Returns := rx.Match[1];
Delete(CreateCode, rx.MatchPos[0], rx.MatchLen[0]);
end;
rx.Expression := '\b(NOT\s+)?DETERMINISTIC\b';
if rx.Exec(CreateCode) then begin
Deterministic := rx.MatchLen[1] = -1;
Delete(CreateCode, rx.MatchPos[0], rx.MatchLen[0]);
end;
rx.Expression := '\b(CONTAINS SQL|NO SQL|READS SQL DATA|MODIFIES SQL DATA)\b';
if rx.Exec(CreateCode) then begin
DataAccess := rx.Match[1];
Delete(CreateCode, rx.MatchPos[0], rx.MatchLen[0]);
end;
rx.Expression := '\bSQL\s+SECURITY\s+(DEFINER|INVOKER)\b';
if rx.Exec(CreateCode) then begin
Security := rx.Match[1];
Delete(CreateCode, rx.MatchPos[0], rx.MatchLen[0]);
end;
rx.ModifierG := False;
rx.Expression := '\bCOMMENT\s+''((.+)[^''])''[^'']';
if rx.Exec(CreateCode) then begin
Comment := StringReplace(rx.Match[1], '''''', '''', [rfReplaceAll]);
Delete(CreateCode, rx.MatchPos[0], rx.MatchLen[0]-1);
end;
rx.Expression := '^\s*CHARSET\s+[\w\d]+\s';
if rx.Exec(CreateCode) then
Delete(CreateCode, rx.MatchPos[0], rx.MatchLen[0]-1);
// Tata, remaining code is the routine body
Body := TrimLeft(CreateCode);
rx.Free;
end;
function ReformatSQL(SQL: String): String;
var
AllKeywords, ImportantKeywords: TStringList;

View File

@ -2461,6 +2461,12 @@ object MainForm: TMainForm
ShortCut = 49187
OnExecute = actDataShowAllExecute
end
object actRunRoutines: TAction
Category = 'Database'
Caption = 'Run routine(s) ...'
ImageIndex = 35
OnExecute = actRunRoutinesExecute
end
end
object SaveDialog2: TSaveDialog
DefaultExt = 'reg'
@ -7533,6 +7539,9 @@ object MainForm: TMainForm
object menuEmptyTables: TMenuItem
Action = actEmptyTables
end
object Runroutines1: TMenuItem
Action = actRunRoutines
end
object menuCreateObject: TMenuItem
Caption = 'Create new'
ImageIndex = 130

View File

@ -470,6 +470,8 @@ type
tabDatabases: TTabSheet;
ListDatabases: TVirtualStringTree;
menuFetchDBitems: TMenuItem;
actRunRoutines: TAction;
Runroutines1: TMenuItem;
procedure actCreateDBObjectExecute(Sender: TObject);
procedure menuConnectionsPopup(Sender: TObject);
procedure actExitApplicationExecute(Sender: TObject);
@ -762,6 +764,7 @@ type
procedure ListDatabasesGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode;
Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer);
procedure ListDatabasesDblClick(Sender: TObject);
procedure actRunRoutinesExecute(Sender: TObject);
private
FDelimiter: String;
FileNameSessionLog: String;
@ -921,7 +924,7 @@ type
function GetTreeNodeType(Tree: TBaseVirtualTree; Node: PVirtualNode): TListNodeType;
function GetFocusedTreeNodeType: TListNodeType;
procedure RefreshTree(DoResetTableCache: Boolean; SelectDatabase: String = '');
procedure RefreshTreeDB(db: String);
procedure RefreshTreeDB(db: String; FocusObjectName: String=''; FocusObjectType: TListNodeType=lntNone);
function FindDBNode(db: String): PVirtualNode;
function GridPostUpdate(Sender: TBaseVirtualTree): Boolean;
function GridPostInsert(Sender: TBaseVirtualTree): Boolean;
@ -2545,6 +2548,68 @@ begin
end;
procedure TMainForm.actRunRoutinesExecute(Sender: TObject);
var
Tab: TQueryTab;
Query, ParamInput, ProcOrFunc,
Returns, DataAccess, Security, Comment, Body: String;
Deterministic: Boolean;
i: Integer;
pObj: PDBObject;
Obj: TDBObject;
Objects: TDBObjectList;
Node: PVirtualNode;
Parameters: TRoutineParamList;
begin
// Run stored function(s) or procedure(s)
Objects := TDBObjectList.Create(False);
if ListTables.Focused then begin
Node := ListTables.GetFirstSelected;
while Assigned(Node) do begin
pObj := ListTables.GetNodeData(Node);
if pObj.NodeType in [lntProcedure, lntFunction] then
Objects.Add(pObj^);
Node := ListTables.GetNextSelected(Node);
end;
end else
Objects.Add(SelectedTable);
if Objects.Count = 0 then
MessageDlg('Please select one or more stored function(s) or routine(s).', mtError, [mbOK], 0);
for Obj in Objects do begin
actNewQueryTab.Execute;
Tab := QueryTabs[MainForm.QueryTabs.Count-1];
case Obj.NodeType of
lntProcedure: begin
Query := 'CALL ';
ProcOrFunc := 'PROCEDURE';
end;
lntFunction: begin
Query := 'SELECT ';
ProcOrFunc := 'FUNCTION';
end;
end;
Parameters := TRoutineParamList.Create;
ParseRoutineStructure(Connection.GetVar('SHOW CREATE '+ProcOrFunc+' '+mask(Obj.Name), 2),
Parameters,
Deterministic, Returns, DataAccess, Security, Comment, Body
);
Query := Query + mask(Obj.Name);
ParamInput := '';
for i:=0 to Parameters.Count-1 do begin
if ParamInput <> '' then
ParamInput := ParamInput + ', ';
ParamInput := ParamInput + '''' + InputBox(Obj.Name, 'Parameter #'+IntToStr(i+1)+': '+Parameters[i].Name+' ('+Parameters[i].Datatype+')', '') + '''';
end;
Parameters.Free;
Query := Query + '('+ParamInput+')';
Tab.Memo.Text := Query;
actExecuteQueryExecute(Sender);
end;
end;
procedure TMainForm.actNewWindowExecute(Sender: TObject);
begin
debug('perf: new connection clicked.');
@ -4706,6 +4771,7 @@ var
L: Cardinal;
HasFocus, InDBTree: Boolean;
Obj: PDBObject;
NodeType: TListNodeType;
begin
// DBtree and ListTables both use popupDB as menu. Find out which of them was rightclicked.
if Sender is TPopupMenu then
@ -4721,14 +4787,16 @@ begin
L := DBtree.GetNodeLevel(DBtree.FocusedNode)
else
L := 0;
NodeType := GetFocusedTreeNodeType;
actCreateDatabase.Enabled := L = 0;
actCreateTable.Enabled := L in [1,2];
actCreateView.Enabled := L in [1,2];
actCreateRoutine.Enabled := L in [1,2];
actCreateTrigger.Enabled := L in [1,2];
actDropObjects.Enabled := L in [1,2];
actCopyTable.Enabled := HasFocus and (GetFocusedTreeNodeType in [lntTable, lntView]);
actEmptyTables.Enabled := HasFocus and (GetFocusedTreeNodeType in [lntTable, lntView]);
actCopyTable.Enabled := HasFocus and (NodeType in [lntTable, lntView]);
actEmptyTables.Enabled := HasFocus and (NodeType in [lntTable, lntView]);
actRunRoutines.Enabled := HasFocus and (NodeType in [lntProcedure, lntFunction]);
actEditObject.Enabled := L > 0;
// Show certain items which are valid only here
menuTreeExpandAll.Visible := True;
@ -4743,6 +4811,7 @@ begin
actCreateRoutine.Enabled := True;
actDropObjects.Enabled := ListTables.SelectedCount > 0;
actEmptyTables.Enabled := False;
actRunRoutines.Enabled := True;
if HasFocus then begin
Obj := ListTables.GetNodeData(ListTables.FocusedNode);
actEmptyTables.Enabled := Obj.NodeType in [lntTable, lntView];
@ -5167,7 +5236,6 @@ var
SnippetsAccessible : Boolean;
Files: TStringList;
Col: TTableColumn;
Param: String;
begin
ActiveQueryHelpers.Items.BeginUpdate;
ActiveQueryHelpers.Items.Clear;
@ -5200,10 +5268,8 @@ begin
end;
end;
lntFunction, lntProcedure: if Assigned(RoutineEditor) then begin
for i:=0 to RoutineEditor.Parameters.Count-1 do begin
Param := Copy(RoutineEditor.Parameters[i], 1, Pos(DELIM, RoutineEditor.Parameters[i])-1);
ActiveQueryHelpers.Items.Add(Param);
end;
for i:=0 to RoutineEditor.Parameters.Count-1 do
ActiveQueryHelpers.Items.Add(RoutineEditor.Parameters[i].Name);
end;
end;
end;
@ -6579,10 +6645,9 @@ end;
{**
Refresh one database node in the db tree
}
procedure TMainForm.RefreshTreeDB(db: String);
procedure TMainForm.RefreshTreeDB(db: String; FocusObjectName: String=''; FocusObjectType: TListNodeType=lntNone);
var
oldActiveDatabase, oldSelectedTableName: String;
oldSelectedTableType: TListNodeType;
oldActiveDatabase: String;
DBNode, FNode: PVirtualNode;
TableHereHadFocus: Boolean;
DBObjects: TDBObjectList;
@ -6592,12 +6657,15 @@ var
NewColumn: TColumnIndex; var Allowed: Boolean) of object;
begin
debug('RefreshTreeDB()');
oldActiveDatabase := ActiveDatabase;
oldSelectedTableName := SelectedTable.Name;
oldSelectedTableType := SelectedTable.NodeType;
DBNode := FindDBNode(db);
FNode := DBtree.FocusedNode;
TableHereHadFocus := Assigned(FNode) and (FNode.Parent = DBNode);
TableHereHadFocus := Assigned(FNode) and ((FNode.Parent = DBNode) or (FocusObjectName <> ''));
oldActiveDatabase := ActiveDatabase;
if FocusObjectName = '' then begin
// Most cases just go here and focus the old table afterwards
FocusObjectName := SelectedTable.Name;
FocusObjectType := SelectedTable.NodeType;
end;
// Suspend focus changing event, to avoid tab jumping
FocusChangingEvent := DBtree.OnFocusChanging;
FocusChangeEvent := DBtree.OnFocusChanged;
@ -6612,9 +6680,9 @@ begin
DBObjects := Connection.GetDBObjects(db);
for i:=0 to DBObjects.Count-1 do begin
// Need to check if table was renamed, in which case oldSelectedTable is no longer available
if (DBObjects[i].Name = oldSelectedTableName)
and (DBObjects[i].NodeType = oldSelectedTableType) then begin
SelectDBObject(oldSelectedTableName, oldSelectedTableType);
if (DBObjects[i].Name = FocusObjectName)
and (DBObjects[i].NodeType = FocusObjectType) then begin
SelectDBObject(FocusObjectName, FocusObjectType);
break;
end;
end;

View File

@ -351,4 +351,14 @@ object frmRoutineEditor: TfrmRoutineEditor
end
end
end
object btnRunProc: TButton
Left = 480
Top = 455
Width = 123
Height = 25
Action = MainForm.actRunRoutines
Anchors = [akRight, akBottom]
Images = MainForm.ImageListMain
TabOrder = 5
end
end

View File

@ -38,6 +38,7 @@ type
btnRemoveParam: TToolButton;
btnClearParams: TToolButton;
SynMemoCREATEcode: TSynMemo;
btnRunProc: TButton;
procedure comboTypeSelect(Sender: TObject);
procedure btnSaveClick(Sender: TObject);
procedure btnHelpClick(Sender: TObject);
@ -75,7 +76,7 @@ type
function ComposeCreateStatement(NameOfObject: String): String;
public
{ Public declarations }
Parameters: TStringList;
Parameters: TRoutineParamList;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Init(ObjectName: String=''; ObjectType: TListNodeType=lntNone); override;
@ -111,7 +112,7 @@ begin
Mainform.SynCompletionProposal.AddEditor(SynMemoBody);
FixVT(listParameters);
Mainform.RestoreListSetup(listParameters);
Parameters := TStringList.Create;
Parameters := TRoutineParamList.Create;
editName.MaxLength := NAME_LEN;
end;
@ -127,11 +128,8 @@ end;
procedure TfrmRoutineEditor.Init(ObjectName: String=''; ObjectType: TListNodeType=lntNone);
var
Create, Params: String;
ParenthesesCount: Integer;
Context: String;
rx: TRegExpr;
i: Integer;
Create, Returns, DataAccess, Security, Comment, Body: String;
Deterministic: Boolean;
begin
inherited;
if ObjectType = lntProcedure then FAlterRoutineType := 'PROCEDURE'
@ -149,84 +147,16 @@ begin
if FEditObjectName <> '' then begin
// Editing existing routine
comboType.ItemIndex := ListIndexByRegExpr(comboType.Items, '^'+FAlterRoutineType+'\b');
Create := Mainform.Connection.GetVar('SHOW CREATE '+FAlterRoutineType+' '+Mainform.mask(editName.Text), 2);
rx := TRegExpr.Create;
rx.ModifierI := True;
rx.ModifierG := True;
// CREATE DEFINER=`root`@`localhost` PROCEDURE `bla2`(IN p1 INT, p2 VARCHAR(20))
// CREATE DEFINER=`root`@`localhost` FUNCTION `test3`(`?b` varchar(20)) RETURNS tinyint(4)
// CREATE DEFINER=`root`@`localhost` PROCEDURE `test3`(IN `Param1` int(1) unsigned)
ParenthesesCount := 0;
for i:=1 to Length(Create) do begin
if Create[i] = ')' then begin
Dec(ParenthesesCount);
if ParenthesesCount = 0 then
break;
end;
if ParenthesesCount >= 1 then
Params := Params + Create[i];
if Create[i] = '(' then
Inc(ParenthesesCount);
end;
rx.Expression := '(^|,)\s*((IN|OUT|INOUT)\s+)?(\S+)\s+([^\s,\(]+(\([^\)]*\))?[^,]*)';
if rx.Exec(Params) then while true do begin
Context := UpperCase(rx.Match[3]);
if Context = '' then
Context := 'IN';
Parameters.Add(WideDequotedStr(rx.Match[4], '`') + DELIM + rx.Match[5] + DELIM + Context);
if not rx.ExecNext then
break;
end;
// Cut left part including parameters, so it's easier to parse the rest
Create := Copy(Create, i+1, MaxInt);
// CREATE PROCEDURE sp_name ([proc_parameter[,...]]) [characteristic ...] routine_body
// CREATE FUNCTION sp_name ([func_parameter[,...]]) RETURNS type [characteristic ...] routine_body
// LANGUAGE SQL
// | [NOT] DETERMINISTIC // IS_DETERMINISTIC
// | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA } // DATA_ACCESS
// | SQL SECURITY { DEFINER | INVOKER } // SECURITY_TYPE
// | COMMENT 'string' // COMMENT
rx.Expression := '\bLANGUAGE SQL\b';
if rx.Exec(Create) then
Delete(Create, rx.MatchPos[0], rx.MatchLen[0]);
rx.Expression := '\bRETURNS\s+(\w+(\([^\)]*\))?)';
if rx.Exec(Create) then begin
comboReturns.Text := rx.Match[1];
Delete(Create, rx.MatchPos[0], rx.MatchLen[0]);
end;
rx.Expression := '\b(NOT\s+)?DETERMINISTIC\b';
if rx.Exec(Create) then begin
chkDeterministic.Checked := rx.MatchLen[1] = -1;
Delete(Create, rx.MatchPos[0], rx.MatchLen[0]);
end;
rx.Expression := '\b('+UpperCase(ImplodeStr('|', comboDataAccess.Items))+')\b';
if rx.Exec(Create) then begin
comboDataAccess.ItemIndex := comboDataAccess.Items.IndexOf(rx.Match[1]);
Delete(Create, rx.MatchPos[0], rx.MatchLen[0]);
end;
rx.Expression := '\bSQL\s+SECURITY\s+(DEFINER|INVOKER)\b';
if rx.Exec(Create) then begin
comboSecurity.ItemIndex := comboSecurity.Items.IndexOf(rx.Match[1]);
Delete(Create, rx.MatchPos[0], rx.MatchLen[0]);
end;
rx.ModifierG := False;
rx.Expression := '\bCOMMENT\s+''((.+)[^''])''[^'']';
if rx.Exec(Create) then begin
editComment.Text := StringReplace(rx.Match[1], '''''', '''', [rfReplaceAll]);
Delete(Create, rx.MatchPos[0], rx.MatchLen[0]-1);
end;
rx.Expression := '^\s*CHARSET\s+[\w\d]+\s';
if rx.Exec(Create) then
Delete(Create, rx.MatchPos[0], rx.MatchLen[0]-1);
// Tata, remaining code is the routine body
Create := TrimLeft(Create);
SynMemoBody.Text := Create;
rx.Free;
ParseRoutineStructure(Create, Parameters, Deterministic, Returns, DataAccess, Security, Comment, Body);
comboReturns.Text := Returns;
chkDeterministic.Checked := Deterministic;
if DataAccess <> '' then
comboDataAccess.ItemIndex := comboDataAccess.Items.IndexOf(DataAccess);
if Security <> '' then
comboSecurity.ItemIndex := comboSecurity.Items.IndexOf(Security);
editComment.Text := Comment;
SynMemoBody.Text := Body;
end else begin
editName.Text := '';
end;
@ -236,6 +166,7 @@ begin
Modified := False;
btnSave.Enabled := Modified;
btnDiscard.Enabled := Modified;
Mainform.actRunRoutines.Enabled := FEditObjectName <> '';
Mainform.ShowStatusMsg;
Screen.Cursor := crDefault;
end;
@ -280,8 +211,14 @@ end;
procedure TfrmRoutineEditor.btnAddParamClick(Sender: TObject);
var
Param: TRoutineParam;
begin
Parameters.Add('Param'+IntToStr(Parameters.Count+1)+DELIM+'INT'+DELIM+'IN');
Param := TRoutineParam.Create;
Param.Name := 'Param'+IntToStr(Parameters.Count+1);
Param.Datatype := 'INT';
Param.Context := 'IN';
Parameters.Add(Param);
// See List.OnPaint:
listParameters.Repaint;
Modification(Sender);
@ -336,16 +273,19 @@ procedure TfrmRoutineEditor.listParametersGetText(Sender: TBaseVirtualTree;
Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
var CellText: String);
var
Values: TStringList;
Param: TRoutineParam;
begin
if Column = 0 then
CellText := IntToStr(Node.Index+1)
else if (Column = 3) and (comboType.ItemIndex = 1) then
Param := Parameters[Node.Index];
case Column of
0: CellText := IntToStr(Node.Index+1);
1: CellText := Param.Name;
2: CellText := Param.Datatype;
3: begin
if comboType.ItemIndex = 1 then
CellText := 'IN' // A function can only have IN parameters
else begin
Values := explode(DELIM, Parameters[Node.Index]);
CellText := Values[Column-1];
FreeAndNil(Values);
else
CellText := Param.Context;
end;
end;
end;
@ -371,16 +311,14 @@ end;
procedure TfrmRoutineEditor.listParametersNewText(Sender: TBaseVirtualTree;
Node: PVirtualNode; Column: TColumnIndex; NewText: String);
var
OldValues: TStringList;
new: String;
Param: TRoutineParam;
begin
OldValues := explode(DELIM, Parameters[Node.Index]);
Param := Parameters[Node.Index];
case Column of
1: new := NewText + DELIM + OldValues[1] + DELIM + OldValues[2];
2: new := OldValues[0] + DELIM + NewText + DELIM + OldValues[2];
3: new := OldValues[0] + DELIM + OldValues[1] + DELIM + NewText;
1: Param.Name := NewText;
2: Param.Datatype := NewText;
3: Param.Context := NewText;
end;
Parameters[Node.Index] := new;
Modification(Sender);
end;
@ -469,6 +407,7 @@ var
allRoutineNames: TStringList;
ProcOrFunc: String;
TargetExists: Boolean;
t: TListNodeType;
begin
// Save changes
Result := mrOk;
@ -514,10 +453,13 @@ begin
FEditObjectName := editName.Text;
FAlterRoutineType := UpperCase(GetFirstWord(comboType.Text));
Mainform.SetEditorTabCaption(Self, FEditObjectName);
Mainform.RefreshTreeDB(Mainform.ActiveDatabase);
if FAlterRoutineType = 'PROCEDURE' then t := lntProcedure
else t := lntFunction;
Mainform.RefreshTreeDB(Mainform.ActiveDatabase, FEditObjectName, t);
Modified := False;
btnSave.Enabled := Modified;
btnDiscard.Enabled := Modified;
Mainform.actRunRoutines.Enabled := True;
except
on E:Exception do begin
MessageDlg(E.Message, mtError, [mbOk], 0);
@ -530,16 +472,14 @@ end;
function TfrmRoutineEditor.ComposeCreateStatement(NameOfObject: String): String;
var
ProcOrFunc: String;
par: TStringList;
i: Integer;
begin
ProcOrFunc := UpperCase(GetFirstWord(comboType.Text));
Result := 'CREATE '+ProcOrFunc+' '+Mainform.mask(NameOfObject)+'(';
for i:=0 to Parameters.Count-1 do begin
par := explode(DELIM, Parameters[i]);
if ProcOrFunc = 'PROCEDURE' then
Result := Result + par[2] + ' ';
Result := Result + Mainform.Mask(par[0]) + ' ' + par[1];
Result := Result + Parameters[i].Context + ' ';
Result := Result + Mainform.Mask(Parameters[i].Name) + ' ' + Parameters[i].Datatype;
if i < Parameters.Count-1 then
Result := Result + ', ';
end;

View File

@ -400,7 +400,7 @@ begin
FEditObjectName := editName.Text;
tabALTERcode.TabVisible := FEditObjectName <> '';
Mainform.SetEditorTabCaption(Self, FEditObjectName);
Mainform.RefreshTreeDB(Mainform.ActiveDatabase);
Mainform.RefreshTreeDB(Mainform.ActiveDatabase, FEditObjectName, lntTable);
Mainform.ParseSelectedTableStructure;
ResetModificationFlags;
AlterCodeValid := False;

View File

@ -175,7 +175,7 @@ begin
Mainform.Connection.Query(sql);
FEditObjectName := editName.Text;
Mainform.SetEditorTabCaption(Self, FEditObjectName);
Mainform.RefreshTreeDB(Mainform.ActiveDatabase);
Mainform.RefreshTreeDB(Mainform.ActiveDatabase, FEditObjectName, lntTrigger);
Modified := False;
btnSave.Enabled := Modified;
btnDiscard.Enabled := Modified;

View File

@ -181,7 +181,7 @@ begin
end;
FEditObjectName := editName.Text;
Mainform.SetEditorTabCaption(Self, FEditObjectName);
Mainform.RefreshTreeDB(Mainform.ActiveDatabase);
Mainform.RefreshTreeDB(Mainform.ActiveDatabase, FEditObjectName, lntView);
Mainform.ParseSelectedTableStructure;
Modified := False;
btnSave.Enabled := Modified;