* Factor more code out of each database object editors into parent TDBObjectEditor.

* Use this chance to add a confirmation dialog when leaving the editors, to ask the user if he wants to save modifications. Fixes issue #1524.
* Also, enhance Mainform.RefreshTreeDB in a way that it does not trigger the OnFocusChange event. Important for the editors when saving changes.
This commit is contained in:
Ansgar Becker
2009-12-14 23:55:36 +00:00
parent cbedf0d163
commit dd398bb101
7 changed files with 213 additions and 147 deletions

View File

@ -147,9 +147,15 @@ type
private private
FModified: Boolean; FModified: Boolean;
procedure SetModified(Value: Boolean); procedure SetModified(Value: Boolean);
protected
FEditObjectName: WideString;
public public
procedure Init(ObjectName: WideString=''; ObjectType: TListNodeType=lntNone); Virtual; Abstract; constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Init(ObjectName: WideString=''; ObjectType: TListNodeType=lntNone); virtual;
procedure DeInit;
property Modified: Boolean read FModified write SetModified; property Modified: Boolean read FModified write SetModified;
procedure ApplyModifications; virtual; abstract;
end; end;
@ -245,7 +251,7 @@ var
implementation implementation
uses main, uVistaFuncs; uses main, uVistaFuncs, table_editor, view, routine_editor, trigger_editor;
type type
CharacterSet = record CharacterSet = record
@ -3236,11 +3242,56 @@ end;
{ *** TDBObjectEditor } { *** TDBObjectEditor }
constructor TDBObjectEditor.Create(AOwner: TComponent);
begin
inherited;
// Do not set alClient via DFM! In conjunction with ExplicitXXX properties that
// repeatedly breaks the GUI layout when you reload the project
Align := alClient;
InheritFont(Font);
end;
destructor TDBObjectEditor.Destroy;
begin
DeInit;
inherited;
end;
procedure TDBObjectEditor.SetModified(Value: Boolean); procedure TDBObjectEditor.SetModified(Value: Boolean);
begin begin
FModified := Value; FModified := Value;
end; end;
procedure TDBObjectEditor.Init(ObjectName: WideString=''; ObjectType: TListNodeType=lntNone);
begin
DeInit;
Mainform.showstatus('Initializing editor ...');
FEditObjectName := ObjectName;
Mainform.SetEditorTabCaption(Self, FEditObjectName);
Screen.Cursor := crHourglass;
MainForm.SetupSynEditors;
end;
procedure TDBObjectEditor.DeInit;
var
Msg, ObjType: WideString;
begin
// Ask for saving modifications
if Modified then begin
if Self is TfrmTableEditor then ObjType := 'table'
else if Self is TfrmView then ObjType := 'view'
else if Self is TfrmRoutineEditor then ObjType := 'routine'
else if Self is TfrmTriggerEditor then ObjType := 'trigger'
else ObjType := '<Unknown Type - should never appear>';
if FEditObjectName <> '' then
Msg := 'Save modified '+ObjType+' "'+FEditObjectName+'"?'
else
Msg := 'Save new '+ObjType+'?';
if MessageDlg(Msg, mtConfirmation, [mbYes, mbNo], 0) = mrYes then
ApplyModifications;
end;
end;
end. end.

View File

@ -1056,6 +1056,20 @@ procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
var var
filename : String; filename : String;
begin begin
// Destroy editors and dialogs. Must be done before connection gets closed, as some destructors do SQL stuff.
FreeAndNil(RoutineEditor);
FreeAndNil(TableToolsDialog);
FreeAndNil(UserManagerForm);
FreeAndNil(ViewEditor);
FreeAndNil(SelectDBObjectForm);
FreeAndNil(SQLHelpForm);
FreeAndNil(OptionsForm);
FreeAndNil(SessionManager);
FreeAndNil(TableEditor);
FreeAndNil(TriggerEditor);
FreeAndNil(CreateDatabaseForm);
// Close database connection
DoDisconnect; DoDisconnect;
OpenRegistry; OpenRegistry;
@ -1086,18 +1100,6 @@ begin
SaveListSetup(ListCommandStats); SaveListSetup(ListCommandStats);
SaveListSetup(ListTables); SaveListSetup(ListTables);
FreeAndNil(RoutineEditor);
FreeAndNil(TableToolsDialog);
FreeAndNil(UserManagerForm);
FreeAndNil(ViewEditor);
FreeAndNil(SelectDBObjectForm);
FreeAndNil(SQLHelpForm);
FreeAndNil(OptionsForm);
FreeAndNil(SessionManager);
FreeAndNil(TableEditor);
FreeAndNil(TriggerEditor);
FreeAndNil(CreateDatabaseForm);
debug('mem: clearing query and browse data.'); debug('mem: clearing query and browse data.');
SetLength(DataGridResult.Rows, 0); SetLength(DataGridResult.Rows, 0);
SetLength(DataGridResult.Columns, 0); SetLength(DataGridResult.Columns, 0);
@ -6286,15 +6288,39 @@ end;
procedure TMainForm.RefreshTreeDB(db: WideString); procedure TMainForm.RefreshTreeDB(db: WideString);
var var
oldActiveDatabase: WideString; oldActiveDatabase: WideString;
dbnode: PVirtualNode; DBNode, FNode: PVirtualNode;
oldSelectedTable: TListNode;
TableHereHadFocus: Boolean;
Results: TMySQLQuery;
FocusChangeEvent: procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex) of object;
begin begin
oldActiveDatabase := ActiveDatabase; oldActiveDatabase := ActiveDatabase;
DBtree.ClearSelection; oldSelectedTable := SelectedTable;
DBNode := FindDBNode(db); DBNode := FindDBNode(db);
FNode := DBtree.FocusedNode;
TableHereHadFocus := FNode.Parent = DBNode;
// Suspend focus changing event, to avoid tab jumping
FocusChangeEvent := DBtree.OnFocusChanged;
DBtree.OnFocusChanged := nil;
// Refresh db node
RefreshDbTableList(db); RefreshDbTableList(db);
DBTree.ReinitNode(dbnode, true); DBTree.ReinitNode(DBNode, true);
DBtree.InvalidateChildren(dbnode, false); DBtree.InvalidateChildren(DBNode, false);
ActiveDatabase := oldActiveDatabase; // Set focus on previously focused table node
if TableHereHadFocus then begin
Results := FetchDbTableList(db);
while not Results.Eof do begin
// Need to check if table was renamed, in which case oldSelectedTable is no longer available
if (Results.Col(DBO_NAME) = oldSelectedTable.Text)
and (GetDBObjectType(Results) = oldSelectedTable.NodeType) then begin
SelectDBObject(oldSelectedTable.Text, oldSelectedTable.NodeType);
break;
end;
Results.Next;
end;
end;
// Reactivate focus changing event
DBtree.OnFocusChanged := FocusChangeEvent;
end; end;

View File

@ -85,7 +85,7 @@ object frmRoutineEditor: TfrmRoutineEditor
Caption = 'Save' Caption = 'Save'
Default = True Default = True
TabOrder = 12 TabOrder = 12
OnClick = PostChanges OnClick = btnSaveClick
end end
object btnDiscard: TButton object btnDiscard: TButton
Left = 84 Left = 84

View File

@ -36,7 +36,7 @@ type
lblSQLcode: TLabel; lblSQLcode: TLabel;
SynMemoBody: TSynMemo; SynMemoBody: TSynMemo;
procedure comboTypeSelect(Sender: TObject); procedure comboTypeSelect(Sender: TObject);
procedure PostChanges(Sender: TObject); procedure btnSaveClick(Sender: TObject);
procedure btnHelpClick(Sender: TObject); procedure btnHelpClick(Sender: TObject);
procedure editNameChange(Sender: TObject); procedure editNameChange(Sender: TObject);
procedure btnAddParamClick(Sender: TObject); procedure btnAddParamClick(Sender: TObject);
@ -69,12 +69,12 @@ type
private private
{ Private declarations } { Private declarations }
Parameters: TWideStringList; Parameters: TWideStringList;
FAlterRoutineName: WideString;
FAlterRoutineType: String; FAlterRoutineType: String;
public public
{ Public declarations } { Public declarations }
constructor Create(AOwner: TComponent); override; constructor Create(AOwner: TComponent); override;
procedure Init(ObjectName: WideString=''; ObjectType: TListNodeType=lntNone); override; procedure Init(ObjectName: WideString=''; ObjectType: TListNodeType=lntNone); override;
procedure ApplyModifications; override;
end; end;
@ -92,8 +92,7 @@ constructor TfrmRoutineEditor.Create(AOwner: TComponent);
var var
i: Integer; i: Integer;
begin begin
inherited Create(AOwner); inherited;
Align := alClient;
// Combo items in a .dfm are sporadically lost after an IDE restart, // Combo items in a .dfm are sporadically lost after an IDE restart,
// so we set them here to avoid developer annoyance // so we set them here to avoid developer annoyance
comboType.Items.Add('Procedure (doesn''t return a result)'); comboType.Items.Add('Procedure (doesn''t return a result)');
@ -106,8 +105,6 @@ begin
comboSecurity.Items.Add('Invoker'); comboSecurity.Items.Add('Invoker');
for i := Low(Datatypes) to High(Datatypes) do for i := Low(Datatypes) to High(Datatypes) do
comboReturns.Items.Add(Datatypes[i].Name); comboReturns.Items.Add(Datatypes[i].Name);
SetWindowSizeGrip(Handle, True);
InheritFont(Font);
Mainform.SynCompletionProposal.AddEditor(SynMemoBody); Mainform.SynCompletionProposal.AddEditor(SynMemoBody);
FixVT(listParameters); FixVT(listParameters);
Parameters := TWideStringList.Create; Parameters := TWideStringList.Create;
@ -124,11 +121,10 @@ var
rx: TRegExpr; rx: TRegExpr;
i: Integer; i: Integer;
begin begin
MainForm.SetupSynEditors; inherited;
FAlterRoutineName := ObjectName;
if ObjectType = lntProcedure then FAlterRoutineType := 'PROCEDURE' if ObjectType = lntProcedure then FAlterRoutineType := 'PROCEDURE'
else FAlterRoutineType := 'FUNCTION'; else FAlterRoutineType := 'FUNCTION';
editName.Text := FAlterRoutineName; editName.Text := FEditObjectName;
comboType.ItemIndex := 0; comboType.ItemIndex := 0;
comboReturns.Text := ''; comboReturns.Text := '';
listParameters.Clear; listParameters.Clear;
@ -137,16 +133,15 @@ begin
comboSecurity.ItemIndex := 0; comboSecurity.ItemIndex := 0;
editComment.Clear; editComment.Clear;
SynMemoBody.Text := 'BEGIN'+CRLF+CRLF+'END'; SynMemoBody.Text := 'BEGIN'+CRLF+CRLF+'END';
if FAlterRoutineName <> '' then begin if FEditObjectName <> '' then begin
// Editing existing routine // Editing existing routine
Mainform.SetEditorTabCaption(Self, FAlterRoutineName);
Results := Mainform.Connection.GetResults('SELECT * FROM '+DBNAME_INFORMATION_SCHEMA+'.ROUTINES'+ Results := Mainform.Connection.GetResults('SELECT * FROM '+DBNAME_INFORMATION_SCHEMA+'.ROUTINES'+
' WHERE ROUTINE_SCHEMA='+esc(Mainform.ActiveDatabase)+ ' WHERE ROUTINE_SCHEMA='+esc(Mainform.ActiveDatabase)+
' AND ROUTINE_NAME='+esc(FAlterRoutineName)+ ' AND ROUTINE_NAME='+esc(FEditObjectName)+
' AND ROUTINE_TYPE='+esc(FAlterRoutineType) ' AND ROUTINE_TYPE='+esc(FAlterRoutineType)
); );
if Results.RecordCount <> 1 then if Results.RecordCount <> 1 then
Exception.Create('Cannot find properties of stored routine '+FAlterRoutineName); Exception.Create('Cannot find properties of stored routine '+FEditObjectName);
comboType.ItemIndex := ListIndexByRegExpr(comboType.Items, '^'+FAlterRoutineType+'\b'); comboType.ItemIndex := ListIndexByRegExpr(comboType.Items, '^'+FAlterRoutineType+'\b');
chkDeterministic.Checked := Results.Col('IS_DETERMINISTIC') = 'YES'; chkDeterministic.Checked := Results.Col('IS_DETERMINISTIC') = 'YES';
comboReturns.Text := Results.Col('DTD_IDENTIFIER'); comboReturns.Text := Results.Col('DTD_IDENTIFIER');
@ -185,7 +180,6 @@ begin
FreeAndNil(Results); FreeAndNil(Results);
end else begin end else begin
editName.Text := 'Enter routine name'; editName.Text := 'Enter routine name';
Mainform.SetEditorTabCaption(Self, '');
end; end;
editNameChange(Self); editNameChange(Self);
comboTypeSelect(comboType); comboTypeSelect(comboType);
@ -403,7 +397,14 @@ begin
end; end;
procedure TfrmRoutineEditor.PostChanges(Sender: TObject); procedure TfrmRoutineEditor.btnSaveClick(Sender: TObject);
begin
// Apply or OK button clicked
ApplyModifications;
end;
procedure TfrmRoutineEditor.ApplyModifications;
var var
BaseSQL, TempSQL, FinalSQL, TempName: WideString; BaseSQL, TempSQL, FinalSQL, TempName: WideString;
i: Integer; i: Integer;
@ -411,18 +412,8 @@ var
ProcOrFunc: String; ProcOrFunc: String;
TargetExists: Boolean; TargetExists: Boolean;
begin begin
// Apply or OK button clicked // Save changes
ProcOrFunc := UpperCase(GetFirstWord(comboType.Text)); ProcOrFunc := UpperCase(GetFirstWord(comboType.Text));
if editName.Text = '' then begin
MessageDlg('Please specify the routine''s name.', mtError, [mbOK], 0);
editName.SetFocus;
Exit;
end else if (ProcOrFunc = 'FUNCTION') and (comboReturns.Text = '') then begin
MessageDlg('Please specify the function''s returning datatype.', mtError, [mbOK], 0);
comboReturns.SetFocus;
Exit;
end;
BaseSQL := ''; BaseSQL := '';
for i := 0 to Parameters.Count - 1 do begin for i := 0 to Parameters.Count - 1 do begin
par := explode(DELIM, Parameters[i]); par := explode(DELIM, Parameters[i]);
@ -448,18 +439,18 @@ begin
// Create a temp routine, check for syntax errors, then drop the old routine and create it. // Create a temp routine, check for syntax errors, then drop the old routine and create it.
// See also: http://dev.mysql.com/doc/refman/5.0/en/alter-procedure.html // See also: http://dev.mysql.com/doc/refman/5.0/en/alter-procedure.html
try try
if FAlterRoutineName <> '' then begin if FEditObjectName <> '' then begin
// Create temp name // Create temp name
i := 0; i := 0;
allRoutineNames := Mainform.Connection.GetCol('SELECT ROUTINE_NAME FROM '+Mainform.mask(DBNAME_INFORMATION_SCHEMA)+'.'+Mainform.mask('ROUTINES')+ allRoutineNames := Mainform.Connection.GetCol('SELECT ROUTINE_NAME FROM '+Mainform.mask(DBNAME_INFORMATION_SCHEMA)+'.'+Mainform.mask('ROUTINES')+
' WHERE ROUTINE_SCHEMA = '+esc(Mainform.ActiveDatabase)+ ' WHERE ROUTINE_SCHEMA = '+esc(Mainform.ActiveDatabase)+
' AND ROUTINE_TYPE = '+esc(ProcOrFunc) ' AND ROUTINE_TYPE = '+esc(ProcOrFunc)
); );
TargetExists := ((editName.Text <> FAlterRoutineName) or (ProcOrFunc <> FAlterRoutineType)) and TargetExists := ((editName.Text <> FEditObjectName) or (ProcOrFunc <> FAlterRoutineType)) and
(allRoutineNames.IndexOf(editName.Text) > -1); (allRoutineNames.IndexOf(editName.Text) > -1);
if TargetExists then begin if TargetExists then begin
if MessageDlg('Routine "'+editName.Text+'" already exists. Overwrite it?', if MessageDlg('Routine "'+editName.Text+'" already exists. Overwrite it?',
mtConfirmation, [mbOk, mbCancel], 0) = mrCancel then begin mtConfirmation, [mbYes, mbNo], 0) = mrNo then begin
Exit; Exit;
end; end;
end; end;
@ -474,7 +465,7 @@ begin
// Drop temporary routine, used for syntax checking // Drop temporary routine, used for syntax checking
Mainform.Connection.Query('DROP '+ProcOrFunc+' IF EXISTS '+Mainform.mask(TempName)); Mainform.Connection.Query('DROP '+ProcOrFunc+' IF EXISTS '+Mainform.mask(TempName));
// Drop edited routine // Drop edited routine
Mainform.Connection.Query('DROP '+FAlterRoutineType+' IF EXISTS '+Mainform.mask(FAlterRoutineName)); Mainform.Connection.Query('DROP '+FAlterRoutineType+' IF EXISTS '+Mainform.mask(FEditObjectName));
if TargetExists then begin if TargetExists then begin
// Drop target routine - overwriting has been confirmed, see above // Drop target routine - overwriting has been confirmed, see above
Mainform.Connection.Query('DROP '+ProcOrFunc+' IF EXISTS '+Mainform.mask(editName.Text)); Mainform.Connection.Query('DROP '+ProcOrFunc+' IF EXISTS '+Mainform.mask(editName.Text));
@ -483,10 +474,10 @@ begin
FinalSQL := 'CREATE '+ProcOrFunc+' '+Mainform.mask(editName.Text)+'(' + BaseSQL; FinalSQL := 'CREATE '+ProcOrFunc+' '+Mainform.mask(editName.Text)+'(' + BaseSQL;
Mainform.Connection.Query(FinalSQL); Mainform.Connection.Query(FinalSQL);
// Set editing name if create/alter query was successful // Set editing name if create/alter query was successful
FAlterRoutineName := editName.Text; FEditObjectName := editName.Text;
FAlterRoutineType := ProcOrFunc; FAlterRoutineType := UpperCase(GetFirstWord(comboType.Text));
Mainform.SetEditorTabCaption(Self, FAlterRoutineName); Mainform.SetEditorTabCaption(Self, FEditObjectName);
Mainform.actRefresh.Execute; Mainform.RefreshTreeDB(Mainform.ActiveDatabase);
Modified := False; Modified := False;
btnSave.Enabled := Modified; btnSave.Enabled := Modified;
btnDiscard.Enabled := Modified; btnDiscard.Enabled := Modified;
@ -501,9 +492,10 @@ procedure TfrmRoutineEditor.btnDiscardClick(Sender: TObject);
var var
t: TListNodeType; t: TListNodeType;
begin begin
Modified := False;
if FAlterRoutineType = 'PROCEDURE' then t := lntProcedure if FAlterRoutineType = 'PROCEDURE' then t := lntProcedure
else t := lntFunction; else t := lntFunction;
Init(FAlterRoutineName, t); Init(FEditObjectName, t);
end; end;

View File

@ -170,7 +170,6 @@ type
private private
{ Private declarations } { Private declarations }
FLoaded: Boolean; FLoaded: Boolean;
FAlterTableName: WideString;
CreateCodeValid, AlterCodeValid: Boolean; CreateCodeValid, AlterCodeValid: Boolean;
FColumns, FKeys, FForeignKeys: TObjectList; FColumns, FKeys, FForeignKeys: TObjectList;
DeletedKeys, DeletedForeignKeys: TWideStringList; DeletedKeys, DeletedForeignKeys: TWideStringList;
@ -190,6 +189,7 @@ type
constructor Create(AOwner: TComponent); override; constructor Create(AOwner: TComponent); override;
destructor Destroy; override; destructor Destroy; override;
procedure Init(ObjectName: WideString=''; ObjectType: TListNodeType=lntNone); override; procedure Init(ObjectName: WideString=''; ObjectType: TListNodeType=lntNone); override;
procedure ApplyModifications; override;
end; end;
@ -207,12 +207,8 @@ const
constructor TfrmTableEditor.Create(AOwner: TComponent); constructor TfrmTableEditor.Create(AOwner: TComponent);
begin begin
inherited Create(AOwner); inherited;
// Do not set alClient via DFM! In conjunction with ExplicitXXX properties that
// repeatedly breaks the GUI layout when you reload the project
Align := alClient;
PageControlMain.Height := GetRegValue(REGNAME_TABLEEDITOR_TABSHEIGHT, PageControlMain.Height); PageControlMain.Height := GetRegValue(REGNAME_TABLEEDITOR_TABSHEIGHT, PageControlMain.Height);
InheritFont(Font);
FixVT(listColumns); FixVT(listColumns);
FixVT(treeIndexes); FixVT(treeIndexes);
FixVT(listForeignKeys); FixVT(listForeignKeys);
@ -243,7 +239,7 @@ begin
Mainform.SaveListSetup(listColumns); Mainform.SaveListSetup(listColumns);
Mainform.SaveListSetup(treeIndexes); Mainform.SaveListSetup(treeIndexes);
Mainform.SaveListSetup(listForeignKeys); Mainform.SaveListSetup(listForeignKeys);
Inherited; inherited;
end; end;
@ -252,22 +248,16 @@ var
CreateTable, AttrName, AttrValue: WideString; CreateTable, AttrName, AttrValue: WideString;
rx: TRegExpr; rx: TRegExpr;
begin begin
Mainform.showstatus('Initializing editor ...'); inherited;
Screen.Cursor := crHourglass;
FLoaded := False; FLoaded := False;
// Start with "basic" tab activated when just called
if FAlterTableName <> ObjectName then
PageControlMain.ActivePage := tabBasic;
comboEngine.Items := Mainform.Connection.TableEngines; comboEngine.Items := Mainform.Connection.TableEngines;
comboEngine.ItemIndex := comboEngine.Items.IndexOf(Mainform.Connection.TableEngineDefault); comboEngine.ItemIndex := comboEngine.Items.IndexOf(Mainform.Connection.TableEngineDefault);
comboCollation.Items := Mainform.Connection.CollationList; comboCollation.Items := Mainform.Connection.CollationList;
FAlterTableName := ObjectName;
listColumns.BeginUpdate; listColumns.BeginUpdate;
FColumns.Clear; FColumns.Clear;
btnClearIndexesClick(Self); btnClearIndexesClick(Self);
btnClearForeignKeysClick(Self); btnClearForeignKeysClick(Self);
tabALTERcode.TabVisible := FAlterTableName <> ''; tabALTERcode.TabVisible := FEditObjectName <> '';
MainForm.SetupSynEditors;
// Clear value editors // Clear value editors
memoComment.Text := ''; memoComment.Text := '';
editAutoInc.Text := ''; editAutoInc.Text := '';
@ -279,16 +269,14 @@ begin
memoUnionTables.Clear; memoUnionTables.Clear;
comboInsertMethod.ItemIndex := -1; comboInsertMethod.ItemIndex := -1;
if FAlterTableName = '' then begin if FEditObjectName = '' then begin
// Creating new table // Creating new table
editName.Text := 'Enter table name'; editName.Text := 'Enter table name';
Mainform.SetEditorTabCaption(Self, '');
comboCollation.ItemIndex := comboCollation.Items.IndexOf(Mainform.Connection.GetVar('SHOW VARIABLES LIKE ''collation_database''', 1)); comboCollation.ItemIndex := comboCollation.Items.IndexOf(Mainform.Connection.GetVar('SHOW VARIABLES LIKE ''collation_database''', 1));
PageControlMain.ActivePage := tabBasic; PageControlMain.ActivePage := tabBasic;
end else begin end else begin
// Editing existing table // Editing existing table
editName.Text := FAlterTableName; editName.Text := FEditObjectName;
Mainform.SetEditorTabCaption(Self, FAlterTableName);
CreateTable := Mainform.SelectedTableCreateStatement; CreateTable := Mainform.SelectedTableCreateStatement;
rx := TRegExpr.Create; rx := TRegExpr.Create;
rx.ModifierI := True; rx.ModifierI := True;
@ -347,43 +335,47 @@ end;
procedure TfrmTableEditor.btnDiscardClick(Sender: TObject); procedure TfrmTableEditor.btnDiscardClick(Sender: TObject);
begin begin
// Reinit GUI, discarding changes // Reinit GUI, discarding changes
Init(FAlterTableName); Modified := False;
Init(FEditObjectName);
end; end;
procedure TfrmTableEditor.btnSaveClick(Sender: TObject); procedure TfrmTableEditor.btnSaveClick(Sender: TObject);
begin
ApplyModifications;
end;
procedure TfrmTableEditor.ApplyModifications;
var var
sql: WideString; sql: WideString;
i: Integer; i: Integer;
Specs: TWideStringlist; Specs: TWideStringlist;
Key: TForeignKey; Key: TForeignKey;
Col: TTableColumn; Col: TTableColumn;
FocusChangeEvent: procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex) of object;
begin begin
// Create or alter table // Create or alter table
try Specs := TWideStringList.Create;
if FAlterTableName = '' then if FEditObjectName = '' then
sql := ComposeCreateStatement sql := ComposeCreateStatement
else begin else begin
sql := ComposeAlterStatement; sql := ComposeAlterStatement;
// Special case for altered foreign keys: These have to be dropped in a seperate query // Special case for altered foreign keys: These have to be dropped in a seperate query
// otherwise the server would return error 121 "Duplicate key on write or update" // otherwise the server would return error 121 "Duplicate key on write or update"
// See also http://dev.mysql.com/doc/refman/5.1/en/innodb-foreign-key-constraints.html : // See also http://dev.mysql.com/doc/refman/5.1/en/innodb-foreign-key-constraints.html :
// "You cannot add a foreign key and drop a foreign key in separate clauses of a single // "You cannot add a foreign key and drop a foreign key in separate clauses of a single
// ALTER TABLE statement. Separate statements are required." // ALTER TABLE statement. Separate statements are required."
Specs := TWideStringList.Create; for i:=0 to FForeignKeys.Count-1 do begin
for i:=0 to FForeignKeys.Count-1 do begin Key := FForeignKeys[i] as TForeignKey;
Key := FForeignKeys[i] as TForeignKey; if Key.Modified and (not Key.Added) then
if Key.Modified and (not Key.Added) then Specs.Add('DROP FOREIGN KEY '+Mainform.mask(Key.KeyName));
Specs.Add('DROP FOREIGN KEY '+Mainform.mask(Key.KeyName));
end;
if Specs.Count > 0 then
Mainform.Connection.Query('ALTER TABLE '+Mainform.mask(FAlterTableName)+' '+ImplodeStr(', ', Specs));
end; end;
end;
try
if Specs.Count > 0 then
Mainform.Connection.Query('ALTER TABLE '+Mainform.mask(FEditObjectName)+' '+ImplodeStr(', ', Specs));
Mainform.Connection.Query(sql); Mainform.Connection.Query(sql);
// Set table name for altering if Apply was clicked tabALTERcode.TabVisible := FEditObjectName <> '';
FAlterTableName := editName.Text;
tabALTERcode.TabVisible := FAlterTableName <> '';
if chkCharsetConvert.Checked then begin if chkCharsetConvert.Checked then begin
// Autoadjust column collations // Autoadjust column collations
for i:=0 to FColumns.Count-1 do begin for i:=0 to FColumns.Count-1 do begin
@ -392,15 +384,12 @@ begin
Col.Collation := comboCollation.Text; Col.Collation := comboCollation.Text;
end; end;
end; end;
ResetModificationFlags; // Set table name for altering if Apply was clicked
FEditObjectName := editName.Text;
// Refresh db tree and reselect edited table node. tabALTERcode.TabVisible := FEditObjectName <> '';
// Supress tab jumping, implicitely invoked by RefreshTreeDB. Mainform.SetEditorTabCaption(Self, FEditObjectName);
FocusChangeEvent := Mainform.DBtree.OnFocusChanged;
Mainform.DBtree.OnFocusChanged := nil;
Mainform.RefreshTreeDB(Mainform.ActiveDatabase); Mainform.RefreshTreeDB(Mainform.ActiveDatabase);
Mainform.DBtree.OnFocusChanged := FocusChangeEvent; ResetModificationFlags;
Mainform.SelectDBObject(FAlterTableName, lntTable);
except except
on E:Exception do on E:Exception do
MessageDlg(E.Message, mtError, [mbOk], 0); MessageDlg(E.Message, mtError, [mbOk], 0);
@ -466,7 +455,7 @@ begin
Mainform.showstatus('Composing ALTER statement ...'); Mainform.showstatus('Composing ALTER statement ...');
Screen.Cursor := crHourglass; Screen.Cursor := crHourglass;
Specs := TWideStringlist.Create; Specs := TWideStringlist.Create;
if editName.Text <> FAlterTableName then if editName.Text <> FEditObjectName then
Specs.Add('RENAME TO ' + Mainform.mask(editName.Text)); Specs.Add('RENAME TO ' + Mainform.mask(editName.Text));
if memoComment.Tag = ModifiedFlag then if memoComment.Tag = ModifiedFlag then
Specs.Add('COMMENT=' + esc(memoComment.Text)); Specs.Add('COMMENT=' + esc(memoComment.Text));
@ -595,7 +584,7 @@ begin
Specs.Add('ADD '+GetForeignKeySQL(i)); Specs.Add('ADD '+GetForeignKeySQL(i));
end; end;
Result := 'ALTER TABLE '+Mainform.mask(FAlterTableName) + CRLF + #9 + ImplodeStr(',' + CRLF + #9, Specs); Result := 'ALTER TABLE '+Mainform.mask(FEditObjectName) + CRLF + #9 + ImplodeStr(',' + CRLF + #9, Specs);
Result := Trim(Result); Result := Trim(Result);
FreeAndNil(Specs); FreeAndNil(Specs);
Mainform.showstatus; Mainform.showstatus;
@ -1731,7 +1720,7 @@ end;
procedure TfrmTableEditor.chkCharsetConvertClick(Sender: TObject); procedure TfrmTableEditor.chkCharsetConvertClick(Sender: TObject);
begin begin
chkCharsetConvert.Enabled := (FAlterTablename <> '') and (comboCollation.ItemIndex > -1); chkCharsetConvert.Enabled := (FEditObjectName <> '') and (comboCollation.ItemIndex > -1);
listColumns.Repaint; listColumns.Repaint;
Modification(Sender); Modification(Sender);
end; end;

View File

@ -31,11 +31,11 @@ type
var CurrentInput: WideString; var x, y: Integer; var CanExecute: Boolean); var CurrentInput: WideString; var x, y: Integer; var CanExecute: Boolean);
private private
{ Private declarations } { Private declarations }
FEditTriggerName: WideString;
public public
{ Public declarations } { Public declarations }
constructor Create(AOwner: TComponent); override; constructor Create(AOwner: TComponent); override;
procedure Init(ObjectName: WideString=''; ObjectType: TListNodeType=lntNone); override; procedure Init(ObjectName: WideString=''; ObjectType: TListNodeType=lntNone); override;
procedure ApplyModifications; override;
end; end;
implementation implementation
@ -53,10 +53,8 @@ var
col: TProposalColumn; col: TProposalColumn;
i: Integer; i: Integer;
begin begin
inherited Create(AOwner); inherited;
Align := alClient;
SynMemoStatement.Highlighter := Mainform.SynSQLSyn1; SynMemoStatement.Highlighter := Mainform.SynSQLSyn1;
InheritFont(Font);
editName.MaxLength := NAME_LEN; editName.MaxLength := NAME_LEN;
comboTiming.Items.Text := 'BEFORE'+CRLF+'AFTER'; comboTiming.Items.Text := 'BEFORE'+CRLF+'AFTER';
comboEvent.Items.Text := 'INSERT'+CRLF+'UPDATE'+CRLF+'DELETE'; comboEvent.Items.Text := 'INSERT'+CRLF+'UPDATE'+CRLF+'DELETE';
@ -78,8 +76,7 @@ procedure TfrmTriggerEditor.Init(ObjectName: WideString=''; ObjectType: TListNod
var var
Definition, TableList: TMySQLQuery; Definition, TableList: TMySQLQuery;
begin begin
Mainform.SetupSynEditors; inherited;
FEditTriggerName := ObjectName;
editName.Text := ''; editName.Text := '';
SynMemoStatement.Text := ''; SynMemoStatement.Text := '';
comboEvent.ItemIndex := 0; comboEvent.ItemIndex := 0;
@ -93,20 +90,18 @@ begin
end; end;
if comboTable.Items.Count > 0 then if comboTable.Items.Count > 0 then
comboTable.ItemIndex := 0; comboTable.ItemIndex := 0;
if FEditTriggerName <> '' then begin if FEditObjectName <> '' then begin
// Edit mode // Edit mode
Mainform.SetEditorTabCaption(Self, FEditTriggerName); editName.Text := FEditObjectName;
editName.Text := FEditTriggerName;
Definition := Mainform.Connection.GetResults('SELECT '+ Definition := Mainform.Connection.GetResults('SELECT '+
'EVENT_MANIPULATION, EVENT_OBJECT_TABLE, ACTION_STATEMENT, ACTION_TIMING '+ 'EVENT_MANIPULATION, EVENT_OBJECT_TABLE, ACTION_STATEMENT, ACTION_TIMING '+
'FROM information_schema.TRIGGERS '+ 'FROM information_schema.TRIGGERS '+
'WHERE TRIGGER_SCHEMA='+esc(Mainform.ActiveDatabase)+' AND TRIGGER_NAME='+esc(FEditTriggerName)); 'WHERE TRIGGER_SCHEMA='+esc(Mainform.ActiveDatabase)+' AND TRIGGER_NAME='+esc(FEditObjectName));
comboTable.ItemIndex := comboTable.Items.IndexOf(Definition.Col('EVENT_OBJECT_TABLE')); comboTable.ItemIndex := comboTable.Items.IndexOf(Definition.Col('EVENT_OBJECT_TABLE'));
comboTiming.ItemIndex := comboTiming.Items.IndexOf(UpperCase(Definition.Col('ACTION_TIMING'))); comboTiming.ItemIndex := comboTiming.Items.IndexOf(UpperCase(Definition.Col('ACTION_TIMING')));
comboEvent.ItemIndex := comboEvent.Items.IndexOf(UpperCase(Definition.Col('EVENT_MANIPULATION'))); comboEvent.ItemIndex := comboEvent.Items.IndexOf(UpperCase(Definition.Col('EVENT_MANIPULATION')));
SynMemoStatement.Text := Definition.Col('ACTION_STATEMENT'); SynMemoStatement.Text := Definition.Col('ACTION_STATEMENT');
end else begin end else begin
Mainform.SetEditorTabCaption(Self, '');
editName.Text := 'Enter trigger name'; editName.Text := 'Enter trigger name';
end; end;
Modified := False; Modified := False;
@ -129,19 +124,25 @@ end;
procedure TfrmTriggerEditor.btnDiscardClick(Sender: TObject); procedure TfrmTriggerEditor.btnDiscardClick(Sender: TObject);
begin begin
// Reinit editor, discarding changes // Reinit editor, discarding changes
Init(FEditTriggerName); Modified := False;
Init(FEditObjectName);
end; end;
procedure TfrmTriggerEditor.btnSaveClick(Sender: TObject); procedure TfrmTriggerEditor.btnSaveClick(Sender: TObject);
begin
ApplyModifications;
end;
procedure TfrmTriggerEditor.ApplyModifications;
var var
sql: WideString; sql: WideString;
FocusChangeEvent: procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex) of object;
begin begin
// Edit mode means we drop the trigger and recreate it, as there is no ALTER TRIGGER. // Edit mode means we drop the trigger and recreate it, as there is no ALTER TRIGGER.
try try
if FEditTriggerName <> '' then if FEditObjectName <> '' then
Mainform.Connection.Query('DROP TRIGGER '+Mainform.mask(FEditTriggerName)); Mainform.Connection.Query('DROP TRIGGER '+Mainform.mask(FEditObjectName));
// CREATE // CREATE
// [DEFINER = { user | CURRENT_USER }] // [DEFINER = { user | CURRENT_USER }]
// TRIGGER trigger_name trigger_time trigger_event // TRIGGER trigger_name trigger_time trigger_event
@ -151,12 +152,12 @@ begin
' ON '+Mainform.mask(comboTable.Text)+ ' ON '+Mainform.mask(comboTable.Text)+
' FOR EACH ROW '+SynMemoStatement.Text; ' FOR EACH ROW '+SynMemoStatement.Text;
Mainform.Connection.Query(sql); Mainform.Connection.Query(sql);
FocusChangeEvent := Mainform.DBtree.OnFocusChanged; FEditObjectName := editName.Text;
Mainform.DBtree.OnFocusChanged := nil; Mainform.SetEditorTabCaption(Self, FEditObjectName);
Mainform.RefreshTreeDB(Mainform.ActiveDatabase); Mainform.RefreshTreeDB(Mainform.ActiveDatabase);
Mainform.DBtree.OnFocusChanged := FocusChangeEvent; Modified := False;
Mainform.SelectDBObject(editName.Text, lntTrigger); btnSave.Enabled := Modified;
Init(editName.Text); btnDiscard.Enabled := Modified;
except on E:Exception do except on E:Exception do
MessageDlg(E.Message, mtError, [mbOK], 0); MessageDlg(E.Message, mtError, [mbOK], 0);
end; end;

View File

@ -26,11 +26,11 @@ type
procedure Modification(Sender: TObject); procedure Modification(Sender: TObject);
private private
{ Private declarations } { Private declarations }
FEditViewName: WideString;
public public
{ Public declarations } { Public declarations }
constructor Create(AOwner: TComponent); override; constructor Create(AOwner: TComponent); override;
procedure Init(ObjectName: WideString=''; ObjectType: TListNodeType=lntNone); override; procedure Init(ObjectName: WideString=''; ObjectType: TListNodeType=lntNone); override;
procedure ApplyModifications; override;
end; end;
@ -46,11 +46,9 @@ uses main;
} }
constructor TfrmView.Create(AOwner: TComponent); constructor TfrmView.Create(AOwner: TComponent);
begin begin
inherited Create(AOwner); inherited;
Align := alClient;
SynMemoSelect.Highlighter := Mainform.SynSQLSyn1; SynMemoSelect.Highlighter := Mainform.SynSQLSyn1;
Mainform.SynCompletionProposal.AddEditor(SynMemoSelect); Mainform.SynCompletionProposal.AddEditor(SynMemoSelect);
InheritFont(Font);
editName.MaxLength := NAME_LEN; editName.MaxLength := NAME_LEN;
end; end;
@ -64,16 +62,15 @@ var
db: WideString; db: WideString;
rx: TRegExpr; rx: TRegExpr;
begin begin
FEditViewName := ObjectName; inherited;
if FEditViewName <> '' then begin if FEditObjectName <> '' then begin
// Edit mode // Edit mode
editName.Text := FEditViewName; editName.Text := FEditObjectName;
Mainform.SetEditorTabCaption(Self, FEditViewName);
db := Mainform.ActiveDatabase; db := Mainform.ActiveDatabase;
Results := Mainform.Connection.GetResults('SELECT * FROM '+Mainform.mask(DBNAME_INFORMATION_SCHEMA)+'.VIEWS ' + Results := Mainform.Connection.GetResults('SELECT * FROM '+Mainform.mask(DBNAME_INFORMATION_SCHEMA)+'.VIEWS ' +
'WHERE TABLE_SCHEMA = '+esc(db)+' AND TABLE_NAME = '+esc(FEditViewName)); 'WHERE TABLE_SCHEMA = '+esc(db)+' AND TABLE_NAME = '+esc(FEditObjectName));
if Results.RecordCount = 0 then if Results.RecordCount = 0 then
raise Exception.Create('Can''t find view definition for "'+FEditViewName+'" in '+DBNAME_INFORMATION_SCHEMA); raise Exception.Create('Can''t find view definition for "'+FEditObjectName+'" in '+DBNAME_INFORMATION_SCHEMA);
// Algorithm is not changeable as we cannot look up its current state! // Algorithm is not changeable as we cannot look up its current state!
rgAlgorithm.Enabled := False; rgAlgorithm.Enabled := False;
rgAlgorithm.ItemIndex := 0; rgAlgorithm.ItemIndex := 0;
@ -88,7 +85,6 @@ begin
rx.Free; rx.Free;
end else begin end else begin
// Create mode // Create mode
Mainform.SetEditorTabCaption(Self, '');
editName.Text := 'Enter view name'; editName.Text := 'Enter view name';
rgAlgorithm.Enabled := True; rgAlgorithm.Enabled := True;
rgAlgorithm.ItemIndex := 0; rgAlgorithm.ItemIndex := 0;
@ -98,7 +94,6 @@ begin
end; end;
// Ensure name is validated // Ensure name is validated
editNameChange(Self); editNameChange(Self);
MainForm.SetupSynEditors;
Modified := False; Modified := False;
btnSave.Enabled := Modified; btnSave.Enabled := Modified;
btnDiscard.Enabled := Modified; btnDiscard.Enabled := Modified;
@ -129,7 +124,7 @@ procedure TfrmView.btnHelpClick(Sender: TObject);
var var
keyword: String; keyword: String;
begin begin
if FEditViewName = '' then if FEditObjectName = '' then
keyword := 'CREATE VIEW' keyword := 'CREATE VIEW'
else else
keyword := 'ALTER VIEW'; keyword := 'ALTER VIEW';
@ -140,24 +135,31 @@ end;
procedure TfrmView.btnDiscardClick(Sender: TObject); procedure TfrmView.btnDiscardClick(Sender: TObject);
begin begin
// Reinit editor, discarding changes // Reinit editor, discarding changes
Init(FEditViewName); Modified := False;
Init(FEditObjectName);
end; end;
{** {**
Apply changes: Compose and execute SQL Apply changes
} }
procedure TfrmView.btnSaveClick(Sender: TObject); procedure TfrmView.btnSaveClick(Sender: TObject);
begin
ApplyModifications;
end;
procedure TfrmView.ApplyModifications;
var var
sql, viewname, renamed: String; sql, viewname, renamed: String;
begin begin
// Compose CREATE or ALTER statement // Save changes
if FEditViewName = '' then begin if FEditObjectName = '' then begin
sql := 'CREATE '; sql := 'CREATE ';
viewname := editName.Text; viewname := editName.Text;
end else begin end else begin
sql := 'ALTER '; sql := 'ALTER ';
viewname := FEditViewName; viewname := FEditObjectName;
end; end;
viewname := Mainform.mask(viewname); viewname := Mainform.mask(viewname);
if rgAlgorithm.Enabled and (rgAlgorithm.ItemIndex > -1) then if rgAlgorithm.Enabled and (rgAlgorithm.ItemIndex > -1) then
@ -169,11 +171,16 @@ begin
try try
Mainform.Connection.Query(sql); Mainform.Connection.Query(sql);
// Probably rename view // Probably rename view
if (FEditViewName <> '') and (FEditViewName <> editName.Text) then begin if (FEditObjectName <> '') and (FEditObjectName <> editName.Text) then begin
renamed := Mainform.mask(editName.Text); renamed := Mainform.mask(editName.Text);
Mainform.Connection.Query('RENAME TABLE '+viewname + ' TO '+renamed); Mainform.Connection.Query('RENAME TABLE '+viewname + ' TO '+renamed);
end; end;
FEditObjectName := editName.Text;
Mainform.SetEditorTabCaption(Self, FEditObjectName);
Mainform.RefreshTreeDB(Mainform.ActiveDatabase); Mainform.RefreshTreeDB(Mainform.ActiveDatabase);
Modified := False;
btnSave.Enabled := Modified;
btnDiscard.Enabled := Modified;
except except
on E:Exception do on E:Exception do
MessageDlg(E.Message, mtError, [mbOk], 0); MessageDlg(E.Message, mtError, [mbOk], 0);