Implement event editor for MySQL 5.1+ servers. Also, simplify some code around database objects and their editors. Fixes issue #1527

This commit is contained in:
Ansgar Becker
2010-04-08 23:16:40 +00:00
parent ea8574bc1a
commit 5f9129bef2
15 changed files with 992 additions and 246 deletions

View File

@ -36,7 +36,8 @@ uses
mysql_api in '..\..\source\mysql_api.pas',
mysql_connection in '..\..\source\mysql_connection.pas',
trigger_editor in '..\..\source\trigger_editor.pas' {frmTriggerEditor: TFrame},
searchreplace in '..\..\source\searchreplace.pas' {frmSearchReplace};
searchreplace in '..\..\source\searchreplace.pas' {frmSearchReplace},
event_editor in '..\..\source\event_editor.pas' {frmEventEditor: TFrame};
{$R ..\..\res\icon.RES}
{$R ..\..\res\version.RES}

View File

@ -198,6 +198,10 @@
<DCCReference Include="..\..\source\searchreplace.pas">
<Form>frmSearchReplace</Form>
</DCCReference>
<DCCReference Include="..\..\source\event_editor.pas">
<Form>frmEventEditor</Form>
<DesignClass>TFrame</DesignClass>
</DCCReference>
<RcCompile Include="..\..\res\updater.rc">
<Form>updater.res</Form>
</RcCompile>

View File

@ -279,6 +279,7 @@ const
ICONINDEX_STOREDFUNCTION = 35;
ICONINDEX_TRIGGER = 137;
ICONINDEX_FUNCTION = 13;
ICONINDEX_EVENT = 80;
ICONINDEX_KEYWORD = 25;
// Size of byte units

355
source/event_editor.dfm Normal file
View File

@ -0,0 +1,355 @@
object frmEventEditor: TfrmEventEditor
Left = 0
Top = 0
Width = 672
Height = 438
TabOrder = 0
DesignSize = (
672
438)
object lblBody: TLabel
AlignWithMargins = True
Left = 3
Top = 175
Width = 666
Height = 13
Align = alTop
Caption = 'Execution body:'
FocusControl = SynMemoBody
end
object SynMemoBody: TSynMemo
AlignWithMargins = True
Left = 3
Top = 194
Width = 666
Height = 214
Margins.Bottom = 30
SingleLineMode = False
Align = alClient
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'Courier New'
Font.Style = []
TabOrder = 1
Gutter.AutoSize = True
Gutter.DigitCount = 2
Gutter.Font.Charset = DEFAULT_CHARSET
Gutter.Font.Color = clWindowText
Gutter.Font.Height = -11
Gutter.Font.Name = 'Courier New'
Gutter.Font.Style = []
Gutter.LeftOffset = 2
Gutter.ShowLineNumbers = True
Highlighter = MainForm.SynSQLSyn1
Lines.Strings = (
'SynMemoBody')
Options = [eoAutoIndent, eoDragDropEditing, eoEnhanceEndKey, eoGroupUndo, eoHideShowScrollbars, eoKeepCaretX, eoShowScrollHint, eoSmartTabDelete, eoSmartTabs, eoTabsToSpaces]
OnChange = Modification
end
object btnHelp: TButton
Left = 3
Top = 410
Width = 75
Height = 25
Anchors = [akLeft, akBottom]
Caption = 'Help'
TabOrder = 2
OnClick = btnHelpClick
end
object btnDiscard: TButton
Left = 84
Top = 410
Width = 75
Height = 25
Anchors = [akLeft, akBottom]
Caption = 'Discard'
TabOrder = 3
OnClick = btnDiscardClick
end
object btnSave: TButton
Left = 165
Top = 410
Width = 75
Height = 25
Anchors = [akLeft, akBottom]
Caption = 'Save'
Default = True
TabOrder = 4
OnClick = btnSaveClick
end
object PageControlMain: TPageControl
AlignWithMargins = True
Left = 3
Top = 3
Width = 666
Height = 166
ActivePage = tabSettings
Align = alTop
Images = MainForm.ImageListMain
TabOrder = 0
OnChange = PageControlMainChange
object tabSettings: TTabSheet
Caption = 'Settings'
ImageIndex = 39
DesignSize = (
658
137)
object lblName: TLabel
Left = 3
Top = 6
Width = 31
Height = 13
Caption = 'Name:'
FocusControl = editName
end
object lblComment: TLabel
Left = 3
Top = 33
Width = 49
Height = 13
Caption = 'Comment:'
end
object editName: TEdit
Left = 88
Top = 3
Width = 567
Height = 21
Anchors = [akLeft, akTop, akRight]
TabOrder = 0
Text = 'editName'
TextHint = 'Enter event name ...'
OnChange = Modification
end
object chkDropAfterExpiration: TCheckBox
Left = 88
Top = 57
Width = 567
Height = 17
Anchors = [akLeft, akTop, akRight]
Caption = 'Drop event after expiration'
TabOrder = 2
OnClick = Modification
end
object editComment: TEdit
Left = 88
Top = 30
Width = 567
Height = 21
Anchors = [akLeft, akTop, akRight]
TabOrder = 1
Text = 'editComment'
OnChange = Modification
end
object grpState: TRadioGroup
Left = 88
Top = 80
Width = 349
Height = 52
Caption = 'State'
Columns = 3
TabOrder = 3
OnClick = Modification
end
end
object tabScheduling: TTabSheet
Caption = 'Timing'
ImageIndex = 80
object radioOnce: TRadioButton
Left = 3
Top = 15
Width = 74
Height = 17
Caption = 'Once, at:'
Checked = True
TabOrder = 0
TabStop = True
OnClick = radioScheduleClick
end
object radioEvery: TRadioButton
Left = 3
Top = 50
Width = 54
Height = 17
Caption = 'Every'
TabOrder = 3
OnClick = radioScheduleClick
end
object dateOnce: TDateTimePicker
Left = 96
Top = 11
Width = 133
Height = 21
Date = 40273.547337048610000000
Time = 40273.547337048610000000
TabOrder = 1
OnChange = Modification
end
object timeOnce: TDateTimePicker
Left = 235
Top = 11
Width = 133
Height = 21
Date = 40273.548026377310000000
Time = 40273.548026377310000000
Kind = dtkTime
TabOrder = 2
OnChange = Modification
end
object editEveryQuantity: TEdit
Left = 96
Top = 48
Width = 53
Height = 21
TabOrder = 4
Text = '1'
OnChange = Modification
end
object udEveryQuantity: TUpDown
Left = 149
Top = 48
Width = 16
Height = 21
Associate = editEveryQuantity
Min = 1
Max = 400000
Position = 1
TabOrder = 5
end
object comboEveryInterval: TComboBox
Left = 172
Top = 48
Width = 133
Height = 21
Style = csDropDownList
TabOrder = 6
OnChange = comboEveryIntervalChange
end
object chkStarts: TCheckBox
Left = 96
Top = 78
Width = 70
Height = 17
Caption = 'Starts at:'
TabOrder = 7
OnClick = chkStartsEndsClick
end
object chkEnds: TCheckBox
Left = 96
Top = 105
Width = 70
Height = 17
Caption = 'Ends at:'
TabOrder = 10
OnClick = chkStartsEndsClick
end
object dateStarts: TDateTimePicker
Left = 171
Top = 75
Width = 133
Height = 21
Date = 40273.548478379630000000
Time = 40273.548478379630000000
TabOrder = 8
OnChange = Modification
end
object timeStarts: TDateTimePicker
Left = 310
Top = 75
Width = 133
Height = 21
Date = 40273.549206851850000000
Time = 40273.549206851850000000
Kind = dtkTime
TabOrder = 9
OnChange = Modification
end
object timeEnds: TDateTimePicker
Left = 310
Top = 102
Width = 133
Height = 21
Date = 40273.549548981480000000
Time = 40273.549548981480000000
Kind = dtkTime
TabOrder = 12
OnChange = Modification
end
object dateEnds: TDateTimePicker
Left = 171
Top = 102
Width = 133
Height = 21
Date = 40273.549452245370000000
Time = 40273.549452245370000000
TabOrder = 11
OnChange = Modification
end
end
object tabCREATEcode: TTabSheet
Caption = 'CREATE code'
ImageIndex = 119
object SynMemoCREATEcode: TSynMemo
Left = 0
Top = 0
Width = 658
Height = 137
SingleLineMode = False
Align = alClient
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'Courier New'
Font.Style = []
TabOrder = 0
Gutter.AutoSize = True
Gutter.DigitCount = 2
Gutter.Font.Charset = DEFAULT_CHARSET
Gutter.Font.Color = clWindowText
Gutter.Font.Height = -11
Gutter.Font.Name = 'Courier New'
Gutter.Font.Style = []
Gutter.LeftOffset = 2
Gutter.ShowLineNumbers = True
Highlighter = MainForm.SynSQLSyn1
Lines.Strings = (
'SynMemoCREATEcode')
Options = [eoAutoIndent, eoAutoSizeMaxScrollWidth, eoDragDropEditing, eoEnhanceEndKey, eoGroupUndo, eoHideShowScrollbars, eoKeepCaretX, eoShowScrollHint, eoSmartTabDelete, eoSmartTabs, eoTabsToSpaces]
ReadOnly = True
end
end
object tabALTERcode: TTabSheet
Caption = 'ALTER code'
ImageIndex = 119
TabVisible = False
object SynMemoALTERcode: TSynMemo
Left = 0
Top = 0
Width = 658
Height = 137
SingleLineMode = False
Align = alClient
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'Courier New'
Font.Style = []
TabOrder = 0
Gutter.AutoSize = True
Gutter.DigitCount = 2
Gutter.Font.Charset = DEFAULT_CHARSET
Gutter.Font.Color = clWindowText
Gutter.Font.Height = -11
Gutter.Font.Name = 'Courier New'
Gutter.Font.Style = []
Gutter.LeftOffset = 2
Gutter.ShowLineNumbers = True
Highlighter = MainForm.SynSQLSyn1
Lines.Strings = (
'SynMemoALTERcode')
Options = [eoAutoIndent, eoAutoSizeMaxScrollWidth, eoDragDropEditing, eoEnhanceEndKey, eoGroupUndo, eoHideShowScrollbars, eoKeepCaretX, eoShowScrollHint, eoSmartTabDelete, eoSmartTabs, eoTabsToSpaces]
ReadOnly = True
end
end
end
end

386
source/event_editor.pas Normal file
View File

@ -0,0 +1,386 @@
unit event_editor;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, SynEdit, SynMemo, SynRegExpr, ComCtrls, ExtCtrls, WideStrUtils,
helpers, mysql_connection;
type
TFrame = TDBObjectEditor;
TfrmEventEditor = class(TFrame)
SynMemoBody: TSynMemo;
btnHelp: TButton;
btnDiscard: TButton;
btnSave: TButton;
lblBody: TLabel;
PageControlMain: TPageControl;
tabSettings: TTabSheet;
tabScheduling: TTabSheet;
tabCREATEcode: TTabSheet;
tabALTERcode: TTabSheet;
lblName: TLabel;
editName: TEdit;
lblComment: TLabel;
chkDropAfterExpiration: TCheckBox;
editComment: TEdit;
grpState: TRadioGroup;
radioOnce: TRadioButton;
radioEvery: TRadioButton;
dateOnce: TDateTimePicker;
timeOnce: TDateTimePicker;
editEveryQuantity: TEdit;
udEveryQuantity: TUpDown;
comboEveryInterval: TComboBox;
chkStarts: TCheckBox;
chkEnds: TCheckBox;
dateStarts: TDateTimePicker;
timeStarts: TDateTimePicker;
timeEnds: TDateTimePicker;
dateEnds: TDateTimePicker;
SynMemoCREATEcode: TSynMemo;
SynMemoALTERcode: TSynMemo;
procedure Modification(Sender: TObject);
procedure radioScheduleClick(Sender: TObject);
procedure btnSaveClick(Sender: TObject);
procedure btnHelpClick(Sender: TObject);
procedure btnDiscardClick(Sender: TObject);
procedure PageControlMainChange(Sender: TObject);
procedure chkStartsEndsClick(Sender: TObject);
procedure comboEveryIntervalChange(Sender: TObject);
private
{ Private declarations }
AlterCodeValid, CreateCodeValid: Boolean;
function ComposeCreateStatement: String;
function ComposeAlterStatement: String;
function ComposeBaseStatement: String;
procedure UpdateSQLcode;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Init(Obj: TDBObject); override;
function ApplyModifications: TModalResult; override;
end;
implementation
uses main;
{$R *.dfm}
constructor TfrmEventEditor.Create(AOwner: TComponent);
begin
inherited;
Mainform.SynCompletionProposal.AddEditor(SynMemoBody);
comboEveryInterval.Items := Explode('|', 'YEAR|QUARTER|MONTH|DAY|HOUR|MINUTE|WEEK|SECOND|YEAR_MONTH|'+
'DAY_HOUR|DAY_MINUTE|DAY_SECOND|HOUR_MINUTE|HOUR_SECOND|MINUTE_SECOND');
grpState.Items := Explode('|', 'Enable|Disable|Disable on slave');
end;
destructor TfrmEventEditor.Destroy;
begin
// Store GUI setup? Nothing yet.
inherited;
end;
procedure TfrmEventEditor.Init(Obj: TDBObject);
var
CreateCode, DateExpr: String;
rx: TRegExpr;
d: TDateTime;
i: Integer;
begin
inherited;
editName.Clear;
editComment.Clear;
chkDropAfterExpiration.Checked := True;
radioEvery.Checked := True;
grpState.ItemIndex := 0;
SynMemoBody.Text := 'BEGIN'+CRLF+CRLF+'END';
dateOnce.Date := Now;
timeOnce.Time := Now;
dateStarts.Date := Now;
timeStarts.Time := Now;
dateEnds.Date := Now;
timeEnds.Time := Now;
udEveryQuantity.Position := 1;
comboEveryInterval.ItemIndex := comboEveryInterval.Items.IndexOf('DAY');
tabALTERcode.TabVisible := False;
if DBObject.Name <> '' then begin
// Edit mode
tabALTERcode.TabVisible := True;
editName.Text := DBObject.Name;
// CREATE EVENT `eventb`
// ON SCHEDULE EVERY 1 DAY STARTS '2010-04-08 08:05:04' ENDS '2010-04-30 01:01:28'
// ON COMPLETION NOT PRESERVE
// ENABLE
// DO BEGIN END
CreateCode := Mainform.Connection.GetVar('SHOW CREATE EVENT '+Mainform.mask(DBObject.Name), 'Create Event');
DateExpr := '[''"]([^''"]+)[''"]';
rx := TRegExpr.Create;
rx.ModifierI := True;
rx.Expression := '\bON\s+SCHEDULE\s+(EVERY|AT)\s+((\d+|''[^'']+'')\s+(\S+)(\s+STARTS\s+'+DateExpr+')?(\s+ENDS\s+'+DateExpr+')?|'+DateExpr+')';
if rx.Exec(CreateCode) then begin
if UpperCase(rx.Match[1]) = 'AT' then begin
radioOnce.Checked := True;
d := Mainform.Connection.ParseDateTime(rx.Match[9]);
dateOnce.DateTime := d;
timeOnce.DateTime := d;
end else begin
radioEvery.Checked := True;
comboEveryInterval.ItemIndex := comboEveryInterval.Items.IndexOf(rx.Match[4]);
comboEveryIntervalChange(Self);
if udEveryQuantity.Enabled then
udEveryQuantity.Position := MakeInt(rx.Match[3])
else
editEveryQuantity.Text := WideDequotedStr(rx.Match[3], '''');
chkStarts.Checked := rx.MatchLen[5] > 0;
if chkStarts.Checked then begin
d := Mainform.Connection.ParseDateTime(rx.Match[6]);
dateStarts.DateTime := d;
timeStarts.DateTime := d;
end;
chkEnds.Checked := rx.MatchLen[7] > 0;
if chkEnds.Checked then begin
d := Mainform.Connection.ParseDateTime(rx.Match[8]);
dateEnds.DateTime := d;
timeEnds.DateTime := d;
end;
end;
Delete(CreateCode, 1, rx.MatchPos[0]+rx.MatchLen[0]);
end;
rx.Expression := '^ON\s+COMPLETION(\s+NOT)?\s+PRESERVE\b';
if rx.Exec(CreateCode) then begin
chkDropAfterExpiration.Checked := rx.MatchLen[1] > 0;
Delete(CreateCode, 1, rx.MatchPos[0]+rx.MatchLen[0]);
end;
for i:=grpState.Items.Count-1 downto 0 do begin
if Pos(UpperCase(grpState.Items[i]), UpperCase(CreateCode)) = 1 then begin
grpState.ItemIndex := i;
Delete(CreateCode, 1, Length(grpState.Items[i]));
break;
end;
end;
rx.Expression := '\bCOMMENT\s+''((.+)[^''])''';
if rx.Exec(CreateCode) then begin
editComment.Text := StringReplace(rx.Match[1], '''''', '''', [rfReplaceAll]);
Delete(CreateCode, rx.MatchPos[0], rx.MatchLen[0]);
end;
rx.Expression := '\bDO\s+(.*)';
if rx.Exec(CreateCode) then
SynMemoBody.Text := rx.Match[1];
rx.Free;
end;
radioScheduleClick(Self);
Modified := False;
btnSave.Enabled := Modified;
btnDiscard.Enabled := Modified;
Mainform.ShowStatusMsg;
Screen.Cursor := crDefault;
end;
procedure TfrmEventEditor.Modification(Sender: TObject);
begin
Modified := True;
btnSave.Enabled := Modified;
btnDiscard.Enabled := Modified;
CreateCodeValid := False;
AlterCodeValid := False;
UpdateSQLcode;
end;
procedure TfrmEventEditor.btnSaveClick(Sender: TObject);
begin
ApplyModifications;
end;
function TfrmEventEditor.ApplyModifications: TModalResult;
var
sql: String;
begin
// Create or alter table
Result := mrOk;
if DBObject.Name = '' then
sql := ComposeCreateStatement
else
sql := ComposeAlterStatement;
try
Mainform.Connection.Query(sql);
DBObject.Name := editName.Text;
tabALTERcode.TabVisible := DBObject.Name <> '';
Mainform.UpdateEditorTab;
Mainform.RefreshTreeDB(Mainform.ActiveDatabase, DBObject.Name, DBObject.NodeType);
Modified := False;
btnSave.Enabled := Modified;
btnDiscard.Enabled := Modified;
AlterCodeValid := False;
CreateCodeValid := False;
UpdateSQLcode;
except
on E:Exception do begin
MessageDlg(E.Message, mtError, [mbOk], 0);
Result := mrAbort;
end;
end;
end;
function TfrmEventEditor.ComposeCreateStatement: String;
begin
Result := 'CREATE EVENT ' + Mainform.mask(editName.Text) + ' ' + ComposeBaseStatement;
end;
function TfrmEventEditor.ComposeAlterStatement: String;
begin
Result := 'ALTER EVENT ' + Mainform.mask(DBObject.Name) + ' ' + ComposeBaseStatement;
end;
function TfrmEventEditor.ComposeBaseStatement: String;
var
d: TDateTime;
Quantity: String;
begin
// Return CREATE EVENT statement
Result := 'ON SCHEDULE' + CRLF + #9#9;
if radioOnce.Checked then begin
d := dateOnce.DateTime;
ReplaceTime(d, timeOnce.DateTime);
Result := Result + 'AT ' + esc(DateTimeToStr(d)) + CRLF;
end else begin
if udEveryQuantity.Enabled then
Quantity := IntToStr(udEveryQuantity.Position)
else
Quantity := esc(editEveryQuantity.Text);
Result := Result + 'EVERY ' + Quantity + ' ' + comboEveryInterval.Text;
if chkStarts.Checked then begin
d := dateStarts.DateTime;
ReplaceTime(d, timeStarts.DateTime);
Result := Result + ' STARTS ' + esc(DateTimeToStr(d));
end;
if chkEnds.Checked then begin
d := dateEnds.DateTime;
ReplaceTime(d, timeEnds.DateTime);
Result := Result + ' ENDS ' + esc(DateTimeToStr(d));
end;
Result := Result + CRLF;
end;
if chkDropAfterExpiration.Checked then
Result := Result + #9 + 'ON COMPLETION NOT PRESERVE'
else
Result := Result + #9 + 'ON COMPLETION PRESERVE';
if (DBObject.Name <> '') and (DBObject.Name <> editName.Text) then
Result := Result + CRLF + #9 + 'RENAME TO ' + MainForm.mask(editName.Text);
Result := Result + CRLF + #9 + UpperCase(grpState.Items[grpState.ItemIndex]);
Result := Result + CRLF + #9 + 'COMMENT ' + esc(editComment.Text);
Result := Result + CRLF + #9 + 'DO ' + SynMemoBody.Text;
end;
procedure TfrmEventEditor.PageControlMainChange(Sender: TObject);
begin
UpdateSQLcode;
end;
procedure TfrmEventEditor.UpdateSQLcode;
var
OldTopLine: Integer;
begin
if (PageControlMain.ActivePage = tabALTERCode) and (not AlterCodeValid) then begin
SynMemoALTERcode.BeginUpdate;
OldTopLine := SynMemoALTERcode.TopLine;
SynMemoALTERcode.Text := ComposeAlterStatement;
SynMemoALTERcode.TopLine := OldTopLine;
SynMemoALTERcode.EndUpdate;
AlterCodeValid := True;
end else if (PageControlMain.ActivePage = tabCREATECode) and (not CreateCodeValid) then begin
SynMemoCREATEcode.BeginUpdate;
OldTopLine := SynMemoCREATEcode.TopLine;
SynMemoCREATEcode.Text := ComposeCreateStatement;
SynMemoCREATEcode.TopLine := OldTopLine;
SynMemoCREATEcode.EndUpdate;
CreateCodeValid := True;
end;
end;
procedure TfrmEventEditor.radioScheduleClick(Sender: TObject);
var
IsOnce: Boolean;
begin
IsOnce := radioOnce.Checked;
dateOnce.Enabled := IsOnce;
timeOnce.Enabled := IsOnce;
editEveryQuantity.Enabled := not IsOnce;
udEveryQuantity.Enabled := not IsOnce;
comboEveryInterval.Enabled := not IsOnce;
comboEveryIntervalChange(Sender);
chkStarts.Enabled := not IsOnce;
chkEnds.Enabled := not IsOnce;
chkStartsEndsClick(Sender);
Modification(Sender);
end;
procedure TfrmEventEditor.chkStartsEndsClick(Sender: TObject);
begin
// Enable/disable start+end controls
dateStarts.Enabled := chkStarts.Checked and chkStarts.Enabled;
timeStarts.Enabled := chkStarts.Checked and chkStarts.Enabled;
dateEnds.Enabled := chkEnds.Checked and chkEnds.Enabled;
timeEnds.Enabled := chkEnds.Checked and chkEnds.Enabled;
Modification(Sender);
end;
procedure TfrmEventEditor.comboEveryIntervalChange(Sender: TObject);
begin
// To enter DAY_HOUR values editor has to accept "1-2"
if Pos('_', comboEveryInterval.Text) > 0 then begin
udEveryQuantity.Associate := nil;
udEveryQuantity.Enabled := False;
end else begin
udEveryQuantity.Associate := editEveryQuantity;
udEveryQuantity.Enabled := True;
end;
Modification(Sender);
end;
procedure TfrmEventEditor.btnDiscardClick(Sender: TObject);
begin
// Reinit editor, discarding changes
Modified := False;
Init(DBObject);
end;
procedure TfrmEventEditor.btnHelpClick(Sender: TObject);
var
keyword: String;
begin
if DBObject.Name = '' then
keyword := 'CREATE EVENT'
else
keyword := 'ALTER EVENT';
Mainform.CallSQLHelpWithKeyword(keyword);
end;
end.

View File

@ -142,15 +142,16 @@ type
FModified: Boolean;
procedure SetModified(Value: Boolean);
protected
FEditObjectName: String;
public
DBObject: TDBObject;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Init(ObjectName: String=''; ObjectType: TListNodeType=lntNone); virtual;
procedure Init(Obj: TDBObject); virtual;
function DeInit: TModalResult;
property Modified: Boolean read FModified write SetModified;
function ApplyModifications: TModalResult; virtual; abstract;
end;
TDBObjectEditorClass = class of TDBObjectEditor;
TWndProc = function (hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
PGripInfo = ^TGripInfo;
@ -271,7 +272,7 @@ var
implementation
uses main, uVistaFuncs, table_editor, view, routine_editor, trigger_editor;
uses main, uVistaFuncs, table_editor, view, routine_editor, trigger_editor, event_editor;
@ -3250,7 +3251,7 @@ begin
Value := esAddedDeleted;
FStatus := Value;
if Value <> esUntouched then
Mainform.TableEditor.Modification(Self);
TfrmTableEditor(Mainform.ActiveObjectEditor).Modification(Self);
end;
@ -3318,11 +3319,12 @@ begin
FModified := Value;
end;
procedure TDBObjectEditor.Init(ObjectName: String=''; ObjectType: TListNodeType=lntNone);
procedure TDBObjectEditor.Init(Obj: TDBObject);
begin
Mainform.ShowStatusMsg('Initializing editor ...');
FEditObjectName := ObjectName;
Mainform.SetEditorTabCaption(Self, FEditObjectName);
ScaleControls(Screen.PixelsPerInch, FORMS_DPI);
DBObject := Obj;
Mainform.UpdateEditorTab;
Screen.Cursor := crHourglass;
MainForm.SetupSynEditors;
end;
@ -3334,13 +3336,9 @@ begin
// Ask for saving modifications
Result := mrOk;
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+'"?'
ObjType := LowerCase(DBObject.ObjType);
if DBObject.Name <> '' then
Msg := 'Save modified '+ObjType+' "'+DBObject.Name+'"?'
else
Msg := 'Save new '+ObjType+'?';
Result := MessageDlg(Msg, mtConfirmation, [mbYes, mbNo, mbCancel], 0);

View File

@ -2467,6 +2467,14 @@ object MainForm: TMainForm
ImageIndex = 35
OnExecute = actRunRoutinesExecute
end
object actCreateEvent: TAction
Category = 'Database'
Caption = 'Event'
Enabled = False
Hint = 'Create new event in selected database'
ImageIndex = 80
OnExecute = actCreateDBObjectExecute
end
end
object SaveDialog2: TSaveDialog
DefaultExt = 'reg'
@ -7563,6 +7571,9 @@ object MainForm: TMainForm
object menuCreateTrigger: TMenuItem
Action = actCreateTrigger
end
object Event1: TMenuItem
Action = actCreateEvent
end
end
object N17: TMenuItem
Caption = '-'

View File

@ -16,7 +16,7 @@ uses
ShlObj, SynEditMiscClasses, SynEditSearch, SynEditRegexSearch, SynCompletionProposal, SynEditHighlighter,
SynHighlighterSQL, Tabs, SynUnicode, SynRegExpr, WideStrUtils, ExtActns,
CommCtrl, Contnrs, Generics.Collections, SynEditExport, SynExportHTML,
routine_editor, trigger_editor, options, EditVar, helpers, createdatabase, table_editor,
routine_editor, trigger_editor, event_editor, options, EditVar, helpers, createdatabase, table_editor,
TableTools, View, Usermanager, SelectDBObject, connections, sqlhelp, mysql_connection,
mysql_api, insertfiles, searchreplace, loaddata, copytable;
@ -472,6 +472,8 @@ type
menuFetchDBitems: TMenuItem;
actRunRoutines: TAction;
Runroutines1: TMenuItem;
actCreateEvent: TAction;
Event1: TMenuItem;
procedure actCreateDBObjectExecute(Sender: TObject);
procedure menuConnectionsPopup(Sender: TObject);
procedure actExitApplicationExecute(Sender: TObject);
@ -781,7 +783,6 @@ type
FilterTextDatabase,
FilterTextData: String;
PreviousFocusedNode: PVirtualNode;
FActiveObjectEditor: TDBObjectEditor;
FCmdlineFilenames: TStringlist;
FCmdlineConnectionParams: TConnectionParameters;
FCmdlineSessionName: String;
@ -796,7 +797,7 @@ type
procedure SetVisibleListColumns( List: TVirtualStringTree; Columns: TStringList );
procedure ToggleFilterPanel(ForceVisible: Boolean = False);
procedure AutoCalcColWidth(Tree: TVirtualStringTree; Column: TColumnIndex);
function PlaceObjectEditor(Which: TListNodeType): TDBObjectEditor;
procedure PlaceObjectEditor(Obj: TDBObject);
procedure SetTabCaption(PageIndex: Integer; Text: String);
function ConfirmTabClose(PageIndex: Integer): Boolean;
procedure SaveQueryMemo(Tab: TQueryTab; Filename: String; OnlySelection: Boolean);
@ -818,19 +819,16 @@ type
DBObjectsMaxSize: Int64;
DBObjectsMaxRows: Int64;
ProcessListMaxTime: Int64;
ActiveObjectEditor: TDBObjectEditor;
// Cached forms
TableToolsDialog: TfrmTableTools;
ViewEditor: TfrmView;
UserManagerForm: TUserManagerForm;
SelectDBObjectForm: TfrmSelectDBObject;
SQLHelpForm: TfrmSQLhelp;
RoutineEditor: TfrmRoutineEditor;
TriggerEditor: TfrmTriggerEditor;
OptionsForm: Toptionsform;
SessionManager: TConnForm;
CreateDatabaseForm: TCreateDatabaseForm;
TableEditor: TfrmTableEditor;
InsertFiles: TfrmInsertFiles;
EditVariableForm: TfrmEditVariable;
SearchReplaceDialog: TfrmSearchReplace;
@ -941,7 +939,7 @@ type
function GetRegKeyTable: String;
procedure SaveListSetup( List: TVirtualStringTree );
procedure RestoreListSetup( List: TVirtualStringTree );
procedure SetEditorTabCaption(Editor: TDBObjectEditor; ObjName: String);
procedure UpdateEditorTab;
procedure SetWindowCaption;
procedure OnMessageHandler(var Msg: TMsg; var Handled: Boolean);
procedure DefaultHandler(var Message); override;
@ -1025,8 +1023,8 @@ begin
Exit;
end;
// Unsaved modified table, trigger, view or routine?
if Assigned(FActiveObjectEditor) then
CanClose := not (FActiveObjectEditor.DeInit in [mrAbort, mrCancel]);
if Assigned(ActiveObjectEditor) then
CanClose := not (ActiveObjectEditor.DeInit in [mrAbort, mrCancel]);
end;
procedure TMainForm.FormDestroy(Sender: TObject);
@ -1035,16 +1033,13 @@ var
i: Integer;
begin
// Destroy editors and dialogs. Must be done before connection gets closed, as some destructors do SQL stuff.
FreeAndNil(RoutineEditor);
FreeAndNil(ActiveObjectEditor);
FreeAndNil(TableToolsDialog);
FreeAndNil(UserManagerForm);
FreeAndNil(ViewEditor);
FreeAndNil(SelectDBObjectForm);
FreeAndNil(SQLHelpForm);
FreeAndNil(OptionsForm);
FreeAndNil(SessionManager);
FreeAndNil(TableEditor);
FreeAndNil(TriggerEditor);
FreeAndNil(CreateDatabaseForm);
FreeAndNil(SearchReplaceDialog);
@ -2487,21 +2482,20 @@ end;
procedure TMainForm.actCreateDBObjectExecute(Sender: TObject);
var
Editor: TDBObjectEditor;
ObjType: TListNodeType;
Obj: TDBObject;
a: TAction;
begin
// Create a new table, view, etc.
tabEditor.TabVisible := True;
PagecontrolMain.ActivePage := tabEditor;
a := Sender as TAction;
ObjType := lntNone;
if a = actCreateTable then ObjType := lntTable
else if a = actCreateView then ObjType := lntView
else if a = actCreateRoutine then ObjType := lntProcedure
else if a = actCreateTrigger then ObjType := lntTrigger;
Editor := PlaceObjectEditor(ObjType);
Editor.Init;
Obj := TDBObject.Create;
if a = actCreateTable then Obj.NodeType := lntTable
else if a = actCreateView then Obj.NodeType := lntView
else if a = actCreateRoutine then Obj.NodeType := lntProcedure
else if a = actCreateTrigger then Obj.NodeType := lntTrigger
else if a = actCreateEvent then Obj.NodeType := lntEvent;
PlaceObjectEditor(Obj);
end;
@ -3817,14 +3811,7 @@ begin
if Column <> (Sender as TVirtualStringTree).Header.MainColumn then
Exit;
Obj := Sender.GetNodeData(Node);
case Obj.NodeType of
lntTable: ImageIndex := ICONINDEX_TABLE;
lntView: ImageIndex := ICONINDEX_VIEW;
lntProcedure: ImageIndex := ICONINDEX_STOREDPROCEDURE;
lntFunction: ImageIndex := ICONINDEX_STOREDFUNCTION;
lntTrigger: ImageIndex := ICONINDEX_TRIGGER;
else ImageIndex := -1;
end;
ImageIndex := Obj.ImageIndex;
end;
@ -4201,18 +4188,9 @@ var
Queries : TStringList;
procedure addTable(Obj: TDBObject);
var Icon: Integer;
begin
case Obj.NodeType of
lntTable: Icon := ICONINDEX_TABLE;
lntFunction: Icon := ICONINDEX_STOREDFUNCTION;
lntProcedure: Icon := ICONINDEX_STOREDPROCEDURE;
lntView: Icon := ICONINDEX_VIEW;
lntTrigger: Icon := ICONINDEX_TRIGGER;
else Icon := -1;
end;
Proposal.InsertList.Add(Obj.Name);
Proposal.ItemList.Add( Format(SYNCOMPLETION_PATTERN, [Icon, LowerCase(Obj.ObjType), Obj.Name]) );
Proposal.ItemList.Add( Format(SYNCOMPLETION_PATTERN, [Obj.ImageIndex, LowerCase(Obj.ObjType), Obj.Name]) );
end;
procedure addColumns( tablename: String );
@ -4795,6 +4773,7 @@ begin
actCreateView.Enabled := L in [1,2];
actCreateRoutine.Enabled := L in [1,2];
actCreateTrigger.Enabled := L in [1,2];
actCreateEvent.Enabled := L in [1,2];
actDropObjects.Enabled := L in [1,2];
actCopyTable.Enabled := HasFocus and (NodeType in [lntTable, lntView]);
actEmptyTables.Enabled := HasFocus and (NodeType in [lntTable, lntView]);
@ -4811,6 +4790,8 @@ begin
actCreateTable.Enabled := True;
actCreateView.Enabled := True;
actCreateRoutine.Enabled := True;
actCreateTrigger.Enabled := True;
actCreateEvent.Enabled := True;
actDropObjects.Enabled := ListTables.SelectedCount > 0;
actEmptyTables.Enabled := False;
actRunRoutines.Enabled := True;
@ -4829,6 +4810,7 @@ begin
actCreateView.Enabled := actCreateView.Enabled and (Connection.ServerVersionInt >= 50001);
actCreateRoutine.Enabled := actCreateRoutine.Enabled and (Connection.ServerVersionInt >= 50003);
actCreateTrigger.Enabled := actCreateTrigger.Enabled and (Connection.ServerVersionInt >= 50002);
actCreateEvent.Enabled := actCreateEvent.Enabled and (Connection.ServerVersionInt >= 50100);
end;
@ -5271,9 +5253,9 @@ begin
ActiveQueryHelpers.Items.Add(Col.Name);
end;
end;
lntFunction, lntProcedure: if Assigned(RoutineEditor) then begin
for i:=0 to RoutineEditor.Parameters.Count-1 do
ActiveQueryHelpers.Items.Add(RoutineEditor.Parameters[i].Name);
lntFunction, lntProcedure: if Assigned(ActiveObjectEditor) then begin
for i:=0 to TfrmRoutineEditor(ActiveObjectEditor).Parameters.Count-1 do
ActiveQueryHelpers.Items.Add(TfrmRoutineEditor(ActiveObjectEditor).Parameters[i].Name);
end;
end;
end;
@ -6318,22 +6300,7 @@ begin
// of DBObjects. Probably a timing issue. Work around that by doing a safety check here.
if Node.Index >= Cardinal(DBObjects.Count) then
Exit;
case DBObjects[Node.Index].NodeType of
lntTable:
if Kind = ikSelected then
ImageIndex := ICONINDEX_TABLE_HIGHLIGHT
else ImageIndex := ICONINDEX_TABLE;
lntView:
if Kind = ikSelected then
ImageIndex := ICONINDEX_VIEW_HIGHLIGHT
else ImageIndex := ICONINDEX_VIEW;
lntProcedure:
ImageIndex := ICONINDEX_STOREDPROCEDURE;
lntFunction:
ImageIndex := ICONINDEX_STOREDFUNCTION;
lntTrigger:
ImageIndex := ICONINDEX_TRIGGER;
end;
ImageIndex := DBObjects[Node.Index].ImageIndex;
end;
end;
end;
@ -6494,7 +6461,7 @@ begin
end;
end;
newDbObject := SelectedTable.Name;
tabEditor.TabVisible := SelectedTable.NodeType in [lntTable, lntView, lntProcedure, lntFunction, lntTrigger];
tabEditor.TabVisible := SelectedTable.NodeType in [lntTable, lntView, lntProcedure, lntFunction, lntTrigger, lntEvent];
tabData.TabVisible := SelectedTable.NodeType in [lntTable, lntView];
ParseSelectedTableStructure;
if tabEditor.TabVisible then begin
@ -6536,8 +6503,8 @@ procedure TMainForm.DBtreeFocusChanging(Sender: TBaseVirtualTree; OldNode,
begin
debug('DBtreeFocusChanging');
// Check if some editor has unsaved changes
if Assigned(FActiveObjectEditor) and Assigned(NewNode) then begin
Allowed := not (FActiveObjectEditor.DeInit in [mrAbort, mrCancel]);
if Assigned(ActiveObjectEditor) and Assigned(NewNode) then begin
Allowed := not (ActiveObjectEditor.DeInit in [mrAbort, mrCancel]);
DBTree.Selected[DBTree.FocusedNode] := not Allowed;
end else
Allowed := True;
@ -8432,67 +8399,39 @@ begin
end;
function TMainForm.PlaceObjectEditor(Which: TListNodeType): TDBObjectEditor;
procedure TMainForm.PlaceObjectEditor(Obj: TDBObject);
var
EditorClass: TDBObjectEditorClass;
begin
// Place the relevant editor frame onto the editor tab, hide all others
Result := nil;
FActiveObjectEditor := nil;
if (not (Which in [lntTable])) and Assigned(TableEditor) then
FreeAndNil(TableEditor);
if (Which <> lntView) and Assigned(ViewEditor) then
FreeAndNil(ViewEditor);
if (not (Which in [lntProcedure, lntFunction])) and Assigned(RoutineEditor) then
FreeAndNil(RoutineEditor);
if (Which <> lntTrigger) and Assigned(TriggerEditor) then
FreeAndNil(TriggerEditor);
if Which in [lntTable] then begin
if not Assigned(TableEditor) then
TableEditor := TfrmTableEditor.Create(tabEditor);
Result := TableEditor;
end else if Which = lntView then begin
if not Assigned(ViewEditor) then
ViewEditor := TfrmView.Create(tabEditor);
Result := ViewEditor;
end else if Which in [lntProcedure, lntFunction] then begin
if not Assigned(RoutineEditor) then
RoutineEditor := TfrmRoutineEditor.Create(tabEditor);
Result := RoutineEditor;
end else if Which = lntTrigger then begin
if not Assigned(TriggerEditor) then
TriggerEditor := TfrmTriggerEditor.Create(tabEditor);
Result := TriggerEditor;
end else
Exit;
Result.Parent := tabEditor;
FActiveObjectEditor := Result;
if Assigned(ActiveObjectEditor) and (Obj.NodeType <> ActiveObjectEditor.DBObject.NodeType) then
FreeAndNil(ActiveObjectEditor);
case Obj.NodeType of
lntTable: EditorClass := TfrmTableEditor;
lntView: EditorClass := TfrmView;
lntProcedure, lntFunction: EditorClass := TfrmRoutineEditor;
lntTrigger: EditorClass := TfrmTriggerEditor;
lntEvent: EditorClass := TfrmEventEditor;
else Exit;
end;
if not Assigned(ActiveObjectEditor) then begin
ActiveObjectEditor := EditorClass.Create(tabEditor);
ActiveObjectEditor.Parent := tabEditor;
end;
ActiveObjectEditor.Init(Obj);
end;
procedure TMainForm.SetEditorTabCaption(Editor: TDBObjectEditor; ObjName: String);
procedure TMainForm.UpdateEditorTab;
var
ObjType, Cap: String;
IconIndex: Integer;
Cap: String;
begin
if Editor = TableEditor then begin
ObjType := 'Table';
IconIndex := ICONINDEX_TABLE;
end else if Editor = ViewEditor then begin
ObjType := 'View';
IconIndex := ICONINDEX_VIEW;
end else if Editor = RoutineEditor then begin
ObjType := 'Routine';
IconIndex := ICONINDEX_STOREDPROCEDURE;
end else if Editor = TriggerEditor then begin
ObjType := 'Trigger';
IconIndex := ICONINDEX_Trigger;
end else
Exit;
tabEditor.ImageIndex := IconIndex;
Cap := ObjType+': ';
if ObjName = '' then
tabEditor.ImageIndex := ActiveObjectEditor.DBObject.ImageIndex;
Cap := ActiveObjectEditor.DBObject.ObjType+': ';
if ActiveObjectEditor.DBObject.Name = '' then
Cap := Cap + '[Untitled]'
else
Cap := sstr(Cap + ObjName, 30);
Cap := sstr(Cap + ActiveObjectEditor.DBObject.Name, 30);
SetTabCaption(tabEditor.PageIndex, Cap);
end;
@ -8510,8 +8449,8 @@ begin
SelectDBObject(Obj.Name, Obj.NodeType);
end;
case GetFocusedTreeNodeType of
lntDb: begin
case DBtree.GetNodeLevel(DBtree.FocusedNode) of
1: begin
if CreateDatabaseForm = nil then
CreateDatabaseForm := TCreateDatabaseForm.Create(Self);
CreateDatabaseForm.modifyDB := ActiveDatabase;
@ -8528,27 +8467,9 @@ begin
end;
end;
lntTable: begin
PlaceObjectEditor(SelectedTable.NodeType);
TableEditor.Init(SelectedTable.Name);
end;
lntView: begin
PlaceObjectEditor(SelectedTable.NodeType);
ViewEditor.Init(SelectedTable.Name);
end;
lntFunction, lntProcedure: begin
PlaceObjectEditor(SelectedTable.NodeType);
RoutineEditor.Init(SelectedTable.Name, SelectedTable.NodeType);
end;
lntTrigger: begin
PlaceObjectEditor(SelectedTable.NodeType);
TriggerEditor.Init(SelectedTable.Name);
end;
2: PlaceObjectEditor(SelectedTable);
end;
end;
@ -9166,6 +9087,17 @@ var
ActiveLineColor: TColor;
Attri: TSynHighlighterAttributes;
Shortcut1, Shortcut2: TShortcut;
procedure FindEditors(Comp: TComponent);
var i: Integer;
begin
for i:=0 to Comp.ComponentCount-1 do begin
if Comp.Components[i] is TSynMemo then
Editors.Add(Comp.Components[i]);
FindEditors(Comp.Components[i]);
end;
end;
begin
// Restore font, highlighter and shortcuts for each instantiated TSynMemo
Editors := TObjectList.Create;
@ -9175,18 +9107,8 @@ begin
Editors.Add(SynMemoFilter);
Editors.Add(SynMemoProcessView);
Editors.Add(SynMemoSQLLog);
if Assigned(TableEditor) then begin
Editors.Add(TableEditor.SynMemoCREATEcode);
Editors.Add(TableEditor.SynMemoALTERcode);
end;
if Assigned(ViewEditor) then
Editors.Add(ViewEditor.SynMemoSelect);
if Assigned(RoutineEditor) then begin
Editors.Add(RoutineEditor.SynMemoBody);
Editors.Add(RoutineEditor.SynMemoCREATEcode);
end;
if Assigned(TriggerEditor) then
Editors.Add(TriggerEditor.SynMemoStatement);
if Assigned(ActiveObjectEditor) then
FindEditors(ActiveObjectEditor);
if Assigned(CreateDatabaseForm) then
Editors.Add(CreateDatabaseForm.SynMemoPreview);
if Assigned(OptionsForm) then

View File

@ -9,13 +9,20 @@ uses
type
{ TDBObjectList and friends }
TListNodeType = (lntNone, lntDb, lntTable, lntView, lntFunction, lntProcedure, lntTrigger, lntColumn);
TListNodeType = (lntNone, lntDb, lntTable, lntView, lntFunction, lntProcedure, lntTrigger, lntEvent, lntColumn);
TListNodeTypes = Set of TListNodeType;
TDBObject = class
Name, Database, Engine, Comment, RowFormat, CreateOptions, Collation, ObjType: String;
TDBObject = class(TObject)
Name, Database, Engine, Comment, RowFormat, CreateOptions, Collation: String;
Created, Updated, LastChecked: TDateTime;
Rows, Size, Version, AvgRowLen, MaxDataLen, IndexLen, DataLen, DataFree, AutoInc, CheckSum: Int64;
NodeType: TListNodeType;
private
function GetObjType: String;
function GetImageIndex: Integer;
public
constructor Create;
property ObjType: String read GetObjType;
property ImageIndex: Integer read GetImageIndex;
end;
PDBObject = ^TDBObject;
TDBObjectList = TObjectList<TDBObject>;
@ -133,7 +140,6 @@ type
function GetInformationSchemaObjects: TStringList;
function GetConnectionUptime: Integer;
function GetServerUptime: Integer;
function ParseDateTime(Str: String): TDateTime;
procedure Log(Category: TMySQLLogCategory; Msg: String);
procedure ClearCache;
procedure SetObjectNamesInSelectedDB;
@ -154,6 +160,7 @@ type
function GetDBObjects(db: String; Refresh: Boolean=False): TDBObjectList;
function GetDBSize(db: String): Int64;
function DbObjectsCached(db: String): Boolean;
function ParseDateTime(Str: String): TDateTime;
procedure ClearDbObjects(db: String);
procedure ClearAllDbObjects;
property Parameters: TConnectionParameters read FParameters write FParameters;
@ -1071,12 +1078,9 @@ begin
Obj.Size := StrToInt64Def(Results.Col('Data_length'), 0) + StrToInt64Def(Results.Col('Index_length'), 0);
Inc(DBSize, Obj.Size);
end;
Obj.ObjType := 'TABLE';
Obj.NodeType := lntTable;
if Results.IsNull(1) and Results.IsNull(2) then begin // Engine column is NULL for views
if Results.IsNull(1) and Results.IsNull(2) then // Engine column is NULL for views
Obj.NodeType := lntView;
Obj.ObjType := 'VIEW';
end;
Obj.Created := ParseDateTime(Results.Col('Create_time'));
Obj.Updated := ParseDateTime(Results.Col('Update_time'));
if Results.ColExists('Type') then
@ -1117,7 +1121,6 @@ begin
obj.Database := db;
obj.Rows := -1;
Obj.Size := -1;
Obj.ObjType := 'FUNCTION';
Obj.NodeType := lntFunction;
Obj.Created := ParseDateTime(Results.Col('Created'));
Obj.Updated := ParseDateTime(Results.Col('Modified'));
@ -1153,7 +1156,6 @@ begin
obj.Database := db;
obj.Rows := -1;
Obj.Size := -1;
Obj.ObjType := 'PROCEDURE';
Obj.NodeType := lntProcedure;
Obj.Created := ParseDateTime(Results.Col('Created'));
Obj.Updated := ParseDateTime(Results.Col('Modified'));
@ -1189,7 +1191,6 @@ begin
obj.Database := db;
obj.Rows := -1;
Obj.Size := -1;
Obj.ObjType := 'TRIGGER';
Obj.NodeType := lntTrigger;
Obj.Created := ParseDateTime(Results.Col('Created'));
Obj.Updated := 0;
@ -1212,6 +1213,41 @@ begin
FreeAndNil(Results);
end;
// Events
if ServerVersionInt >= 50100 then try
Results := GetResults('SHOW EVENTS FROM '+QuoteIdent(db));
except
end;
if Assigned(Results) then begin
while not Results.Eof do begin
obj := TDBObject.Create;
Result.Add(obj);
obj.Name := Results.Col('Name');
obj.Database := db;
obj.Rows := -1;
Obj.Size := -1;
Obj.NodeType := lntEvent;
Obj.Created := 0;
Obj.Updated := 0;
Obj.Engine := '';
Obj.Comment := '';
Obj.Version := -1;
Obj.AutoInc := -1;
Obj.RowFormat := '';
Obj.AvgRowLen := -1;
Obj.MaxDataLen := -1;
Obj.IndexLen := -1;
Obj.DataLen := -1;
Obj.DataFree := -1;
Obj.LastChecked := 0;
Obj.Collation := '';
Obj.CheckSum := -1;
Obj.CreateOptions := '';
Results.Next;
end;
FreeAndNil(Results);
end;
// Sort list like it get sorted in MainForm.vstCompareNodes
Result.Sort;
@ -1468,4 +1504,40 @@ begin
end;
{ TDBObject }
constructor TDBObject.Create;
begin
NodeType := lntNone;
end;
function TDBObject.GetObjType: String;
begin
case NodeType of
lntTable: Result := 'Table';
lntView: Result := 'View';
lntFunction: Result := 'Function';
lntProcedure: Result := 'Procedure';
lntTrigger: Result := 'Trigger';
lntEvent: Result := 'Event';
else Result := 'Unknown, should never appear';
end;
end;
function TDBObject.GetImageIndex: Integer;
begin
// Detect key icon index for specified db object (table, trigger, ...)
case NodeType of
lntTable: Result := ICONINDEX_TABLE;
lntFunction: Result := ICONINDEX_STOREDFUNCTION;
lntProcedure: Result := ICONINDEX_STOREDPROCEDURE;
lntView: Result := ICONINDEX_VIEW;
lntTrigger: Result := ICONINDEX_TRIGGER;
lntEvent: Result := ICONINDEX_EVENT;
else Result := -1;
end;
end;
end.

View File

@ -34,7 +34,7 @@ type
implementation
uses main, helpers;
uses main, helpers, table_editor, mysql_connection;
{$R *.DFM}
@ -78,8 +78,12 @@ begin
else list := Mainform.ListCommandStats;
end;
1: list := Mainform.ListTables;
2: if Assigned(Mainform.TableEditor) and Mainform.TableEditor.Visible then
list := Mainform.TableEditor.listColumns;
2: begin
if Assigned(Mainform.ActiveObjectEditor)
and (Mainform.ActiveObjectEditor.DBObject.NodeType = lntTable)
and Mainform.ActiveObjectEditor.Visible then
list := (Mainform.ActiveObjectEditor as TfrmTableEditor).listColumns;
end;
else list := Mainform.ActiveGrid;
end;
if Assigned(list) then

View File

@ -79,7 +79,7 @@ type
Parameters: TRoutineParamList;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Init(ObjectName: String=''; ObjectType: TListNodeType=lntNone); override;
procedure Init(Obj: TDBObject); override;
function ApplyModifications: TModalResult; override;
end;
@ -126,15 +126,15 @@ begin
end;
procedure TfrmRoutineEditor.Init(ObjectName: String=''; ObjectType: TListNodeType=lntNone);
procedure TfrmRoutineEditor.Init(Obj: TDBObject);
var
Create, Returns, DataAccess, Security, Comment, Body: String;
Deterministic: Boolean;
begin
inherited;
if ObjectType = lntProcedure then FAlterRoutineType := 'PROCEDURE'
if Obj.NodeType = lntProcedure then FAlterRoutineType := 'PROCEDURE'
else FAlterRoutineType := 'FUNCTION';
editName.Text := FEditObjectName;
editName.Text := DBObject.Name;
comboType.ItemIndex := 0;
comboReturns.Text := '';
chkDeterministic.Checked := False;
@ -144,7 +144,7 @@ begin
comboSecurity.ItemIndex := 0;
editComment.Clear;
SynMemoBody.Text := 'BEGIN'+CRLF+CRLF+'END';
if FEditObjectName <> '' then begin
if DBObject.Name <> '' then begin
// Editing existing routine
comboType.ItemIndex := ListIndexByRegExpr(comboType.Items, '^'+FAlterRoutineType+'\b');
Create := Mainform.Connection.GetVar('SHOW CREATE '+FAlterRoutineType+' '+Mainform.mask(editName.Text), 2);
@ -166,7 +166,7 @@ begin
Modified := False;
btnSave.Enabled := Modified;
btnDiscard.Enabled := Modified;
Mainform.actRunRoutines.Enabled := FEditObjectName <> '';
Mainform.actRunRoutines.Enabled := DBObject.Name <> '';
Mainform.ShowStatusMsg;
Screen.Cursor := crDefault;
end;
@ -407,7 +407,6 @@ var
allRoutineNames: TStringList;
ProcOrFunc: String;
TargetExists: Boolean;
t: TListNodeType;
begin
// Save changes
Result := mrOk;
@ -417,14 +416,14 @@ begin
// 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
try
if FEditObjectName <> '' then begin
if DBObject.Name <> '' then begin
// Create temp name
i := 0;
allRoutineNames := Mainform.Connection.GetCol('SELECT ROUTINE_NAME FROM '+Mainform.mask(DBNAME_INFORMATION_SCHEMA)+'.'+Mainform.mask('ROUTINES')+
' WHERE ROUTINE_SCHEMA = '+esc(Mainform.ActiveDatabase)+
' AND ROUTINE_TYPE = '+esc(ProcOrFunc)
);
TargetExists := ((editName.Text <> FEditObjectName) or (ProcOrFunc <> FAlterRoutineType)) and
TargetExists := ((editName.Text <> DBObject.Name) or (ProcOrFunc <> FAlterRoutineType)) and
(allRoutineNames.IndexOf(editName.Text) > -1);
if TargetExists then begin
Result := MessageDlg('Routine "'+editName.Text+'" already exists. Overwrite it?',
@ -442,7 +441,7 @@ begin
// Drop temporary routine, used for syntax checking
Mainform.Connection.Query('DROP '+ProcOrFunc+' IF EXISTS '+Mainform.mask(TempName));
// Drop edited routine
Mainform.Connection.Query('DROP '+FAlterRoutineType+' IF EXISTS '+Mainform.mask(FEditObjectName));
Mainform.Connection.Query('DROP '+FAlterRoutineType+' IF EXISTS '+Mainform.mask(DBObject.Name));
if TargetExists then begin
// Drop target routine - overwriting has been confirmed, see above
Mainform.Connection.Query('DROP '+ProcOrFunc+' IF EXISTS '+Mainform.mask(editName.Text));
@ -450,12 +449,12 @@ begin
end;
Mainform.Connection.Query(ComposeCreateStatement(editName.Text));
// Set editing name if create/alter query was successful
FEditObjectName := editName.Text;
DBObject.Name := editName.Text;
FAlterRoutineType := UpperCase(GetFirstWord(comboType.Text));
Mainform.SetEditorTabCaption(Self, FEditObjectName);
if FAlterRoutineType = 'PROCEDURE' then t := lntProcedure
else t := lntFunction;
Mainform.RefreshTreeDB(Mainform.ActiveDatabase, FEditObjectName, t);
if FAlterRoutineType = 'PROCEDURE' then DBObject.NodeType := lntProcedure
else DBObject.NodeType := lntFunction;
Mainform.UpdateEditorTab;
Mainform.RefreshTreeDB(Mainform.ActiveDatabase, DBObject.Name, DBObject.NodeType);
Modified := False;
btnSave.Enabled := Modified;
btnDiscard.Enabled := Modified;
@ -498,13 +497,9 @@ end;
procedure TfrmRoutineEditor.btnDiscardClick(Sender: TObject);
var
t: TListNodeType;
begin
Modified := False;
if FAlterRoutineType = 'PROCEDURE' then t := lntProcedure
else t := lntFunction;
Init(FEditObjectName, t);
Init(DBObject);
end;

View File

@ -197,7 +197,7 @@ type
{ Public declarations }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Init(ObjectName: String=''; ObjectType: TListNodeType=lntNone); override;
procedure Init(Obj: TDBObject); override;
function ApplyModifications: TModalResult; override;
end;
@ -217,7 +217,6 @@ const
constructor TfrmTableEditor.Create(AOwner: TComponent);
begin
inherited;
ScaleControls(Screen.PixelsPerInch, FORMS_DPI);
PageControlMain.Height := GetRegValue(REGNAME_TABLEEDITOR_TABSHEIGHT, PageControlMain.Height);
FixVT(listColumns);
FixVT(treeIndexes);
@ -253,7 +252,7 @@ begin
end;
procedure TfrmTableEditor.Init(ObjectName: String=''; ObjectType: TListNodeType=lntNone);
procedure TfrmTableEditor.Init(Obj: TDBObject);
var
CreateTable, AttrName, AttrValue: String;
rx: TRegExpr;
@ -267,7 +266,7 @@ begin
FColumns.Clear;
btnClearIndexesClick(Self);
btnClearForeignKeysClick(Self);
tabALTERcode.TabVisible := FEditObjectName <> '';
tabALTERcode.TabVisible := DBObject.Name <> '';
// Clear value editors
memoComment.Text := '';
editAutoInc.Text := '';
@ -279,14 +278,14 @@ begin
memoUnionTables.Clear;
comboInsertMethod.ItemIndex := -1;
if FEditObjectName = '' then begin
if DBObject.Name = '' then begin
// Creating new table
editName.Text := '';
comboCollation.ItemIndex := comboCollation.Items.IndexOf(Mainform.Connection.GetVar('SHOW VARIABLES LIKE ''collation_database''', 1));
PageControlMain.ActivePage := tabBasic;
end else begin
// Editing existing table
editName.Text := FEditObjectName;
editName.Text := DBObject.Name;
CreateTable := Mainform.SelectedTableCreateStatement;
rx := TRegExpr.Create;
rx.ModifierI := True;
@ -350,7 +349,7 @@ procedure TfrmTableEditor.btnDiscardClick(Sender: TObject);
begin
// Reinit GUI, discarding changes
Modified := False;
Init(FEditObjectName);
Init(DBObject);
end;
@ -370,7 +369,7 @@ begin
// Create or alter table
Result := mrOk;
Specs := TStringList.Create;
if FEditObjectName = '' then
if DBObject.Name = '' then
sql := ComposeCreateStatement
else begin
sql := ComposeAlterStatement;
@ -386,9 +385,9 @@ begin
end;
try
if Specs.Count > 0 then
Mainform.Connection.Query('ALTER TABLE '+Mainform.mask(FEditObjectName)+' '+ImplodeStr(', ', Specs));
Mainform.Connection.Query('ALTER TABLE '+Mainform.mask(DBObject.Name)+' '+ImplodeStr(', ', Specs));
Mainform.Connection.Query(sql);
tabALTERcode.TabVisible := FEditObjectName <> '';
tabALTERcode.TabVisible := DBObject.Name <> '';
if chkCharsetConvert.Checked then begin
// Autoadjust column collations
for i:=0 to FColumns.Count-1 do begin
@ -397,10 +396,10 @@ begin
end;
end;
// Set table name for altering if Apply was clicked
FEditObjectName := editName.Text;
tabALTERcode.TabVisible := FEditObjectName <> '';
Mainform.SetEditorTabCaption(Self, FEditObjectName);
Mainform.RefreshTreeDB(Mainform.ActiveDatabase, FEditObjectName, lntTable);
DBObject.Name := editName.Text;
tabALTERcode.TabVisible := DBObject.Name <> '';
Mainform.UpdateEditorTab;
Mainform.RefreshTreeDB(Mainform.ActiveDatabase, DBObject.Name, DBObject.NodeType);
Mainform.ParseSelectedTableStructure;
ResetModificationFlags;
AlterCodeValid := False;
@ -463,7 +462,7 @@ begin
Mainform.ShowStatusMsg('Composing ALTER statement ...');
Screen.Cursor := crHourglass;
Specs := TStringList.Create;
if editName.Text <> FEditObjectName then
if editName.Text <> DBObject.Name then
Specs.Add('RENAME TO ' + Mainform.mask(editName.Text));
if memoComment.Tag = ModifiedFlag then
Specs.Add('COMMENT=' + esc(memoComment.Text));
@ -582,7 +581,7 @@ begin
Specs.Add('ADD '+GetForeignKeySQL(i));
end;
Result := 'ALTER TABLE '+Mainform.mask(FEditObjectName) + CRLF + #9 + ImplodeStr(',' + CRLF + #9, Specs);
Result := 'ALTER TABLE '+Mainform.mask(DBObject.Name) + CRLF + #9 + ImplodeStr(',' + CRLF + #9, Specs);
Result := Trim(Result);
FreeAndNil(Specs);
Mainform.ShowStatusMsg;
@ -1786,7 +1785,7 @@ end;
procedure TfrmTableEditor.chkCharsetConvertClick(Sender: TObject);
begin
chkCharsetConvert.Enabled := (FEditObjectName <> '') and (comboCollation.ItemIndex > -1);
chkCharsetConvert.Enabled := (DBObject.Name <> '') and (comboCollation.ItemIndex > -1);
listColumns.Repaint;
Modification(Sender);
end;
@ -2114,7 +2113,7 @@ begin
2: begin
Key.ReferenceTable := NewText;
if not Key.KeyNameWasCustomized then
Key.KeyName := 'FK_'+FEditObjectName+'_'+Key.ReferenceTable;
Key.KeyName := 'FK_'+DBObject.Name+'_'+Key.ReferenceTable;
end;
3: Key.ForeignColumns := Explode(',', NewText);
4: Key.OnUpdate := NewText;

View File

@ -1031,7 +1031,7 @@ begin
if chkExportTablesDrop.Checked or chkExportTablesCreate.Checked then begin
Output(CRLF+CRLF+'# Dumping structure for '+LowerCase(DBObj.ObjType)+' '+DBObj.Database+'.'+DBObj.Name+CRLF, False, True, True, False, False);
if chkExportTablesDrop.Checked then begin
Struc := 'DROP '+DBObj.ObjType+' IF EXISTS ';
Struc := 'DROP '+UpperCase(DBObj.ObjType)+' IF EXISTS ';
if ToDb then
Struc := Struc + m(FinalDbName)+'.';
Struc := Struc + m(DBObj.Name);
@ -1062,7 +1062,7 @@ begin
lntTrigger: begin
StrucResult := Mainform.Connection.GetResults('SHOW TRIGGERS WHERE `Trigger`='+esc(DBObj.Name));
Struc := 'CREATE '+DBObj.ObjType+' '+m(DBObj.Name)+' '+StrucResult.Col('Timing')+' '+StrucResult.Col('Event')+
Struc := 'CREATE '+UpperCase(DBObj.ObjType)+' '+m(DBObj.Name)+' '+StrucResult.Col('Timing')+' '+StrucResult.Col('Event')+
' ON '+m(StrucResult.Col('Table'))+' FOR EACH ROW '+StrucResult.Col('Statement');
if ToDb then
Insert(m(FinalDbName)+'.', Struc, Pos('TRIGGER', Struc) + 8 );
@ -1076,7 +1076,7 @@ begin
end;
lntFunction, lntProcedure: begin
Struc := Mainform.Connection.GetVar('SHOW CREATE '+DBObj.ObjType+' '+m(DBObj.Database)+'.'+m(DBObj.Name), 2);
Struc := Mainform.Connection.GetVar('SHOW CREATE '+UpperCase(DBObj.ObjType)+' '+m(DBObj.Database)+'.'+m(DBObj.Name), 2);
// Todo: Why exploding?
MultiSQL := Explode(';', Struc);
Struc := ImplodeStr(';'+CRLF, MultiSQL);

View File

@ -34,7 +34,7 @@ type
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
procedure Init(ObjectName: String=''; ObjectType: TListNodeType=lntNone); override;
procedure Init(Obj: TDBObject); override;
function ApplyModifications: TModalResult; override;
end;
@ -54,7 +54,6 @@ var
i: Integer;
begin
inherited;
ScaleControls(Screen.PixelsPerInch, FORMS_DPI);
SynMemoStatement.Highlighter := Mainform.SynSQLSyn1;
editName.MaxLength := NAME_LEN;
comboTiming.Items.Text := 'BEFORE'+CRLF+'AFTER';
@ -73,7 +72,7 @@ begin
end;
procedure TfrmTriggerEditor.Init(ObjectName: String=''; ObjectType: TListNodeType=lntNone);
procedure TfrmTriggerEditor.Init(Obj: TDBObject);
var
Definitions: TMySQLQuery;
DBObjects: TDBObjectList;
@ -93,13 +92,13 @@ begin
end;
if comboTable.Items.Count > 0 then
comboTable.ItemIndex := 0;
if FEditObjectName <> '' then begin
if DBObject.Name <> '' then begin
// Edit mode
editName.Text := FEditObjectName;
editName.Text := DBObject.Name;
Definitions := Mainform.Connection.GetResults('SHOW TRIGGERS FROM '+Mainform.mask(Mainform.ActiveDatabase));
Found := False;
while not Definitions.Eof do begin
if Definitions.Col('Trigger') = FEditObjectName then begin
if Definitions.Col('Trigger') = DBObject.Name then begin
comboTable.ItemIndex := comboTable.Items.IndexOf(Definitions.Col('Table'));
comboTiming.ItemIndex := comboTiming.Items.IndexOf(UpperCase(Definitions.Col('Timing')));
comboEvent.ItemIndex := comboEvent.Items.IndexOf(UpperCase(Definitions.Col('Event')));
@ -138,7 +137,7 @@ procedure TfrmTriggerEditor.btnDiscardClick(Sender: TObject);
begin
// Reinit editor, discarding changes
Modified := False;
Init(FEditObjectName);
Init(DBObject);
end;
@ -160,8 +159,8 @@ begin
// So, we take the risk of loosing the trigger for cases in which the user has SQL errors in
// his statement. The user must fix such errors and re-press "Save" while we have them in memory,
// otherwise the trigger attributes are lost forever.
if FEditObjectName <> '' then try
Mainform.Connection.Query('DROP TRIGGER '+Mainform.mask(FEditObjectName));
if DBObject.Name <> '' then try
Mainform.Connection.Query('DROP TRIGGER '+Mainform.mask(DBObject.Name));
except
end;
// CREATE
@ -173,9 +172,9 @@ begin
' ON '+Mainform.mask(comboTable.Text)+
' FOR EACH ROW '+SynMemoStatement.Text;
Mainform.Connection.Query(sql);
FEditObjectName := editName.Text;
Mainform.SetEditorTabCaption(Self, FEditObjectName);
Mainform.RefreshTreeDB(Mainform.ActiveDatabase, FEditObjectName, lntTrigger);
DBObject.Name := editName.Text;
Mainform.UpdateEditorTab;
Mainform.RefreshTreeDB(Mainform.ActiveDatabase, DBObject.Name, DBObject.NodeType);
Modified := False;
btnSave.Enabled := Modified;
btnDiscard.Enabled := Modified;

View File

@ -29,7 +29,7 @@ type
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
procedure Init(ObjectName: String=''; ObjectType: TListNodeType=lntNone); override;
procedure Init(Obj: TDBObject); override;
function ApplyModifications: TModalResult; override;
end;
@ -47,7 +47,6 @@ uses main;
constructor TfrmView.Create(AOwner: TComponent);
begin
inherited;
ScaleControls(Screen.PixelsPerInch, FORMS_DPI);
SynMemoSelect.Highlighter := Mainform.SynSQLSyn1;
Mainform.SynCompletionProposal.AddEditor(SynMemoSelect);
editName.MaxLength := NAME_LEN;
@ -57,21 +56,21 @@ end;
{**
FormShow: Fill controls with content in edit mode
}
procedure TfrmView.Init(ObjectName: String=''; ObjectType: TListNodeType=lntNone);
procedure TfrmView.Init(Obj: TDBObject);
var
Results: TMySQLQuery;
db: String;
rx: TRegExpr;
begin
inherited;
if FEditObjectName <> '' then begin
if Obj.Name <> '' then begin
// Edit mode
editName.Text := FEditObjectName;
editName.Text := Obj.Name;
db := Mainform.ActiveDatabase;
Results := Mainform.Connection.GetResults('SELECT * FROM '+Mainform.mask(DBNAME_INFORMATION_SCHEMA)+'.VIEWS ' +
'WHERE TABLE_SCHEMA = '+esc(db)+' AND TABLE_NAME = '+esc(FEditObjectName));
'WHERE TABLE_SCHEMA = '+esc(db)+' AND TABLE_NAME = '+esc(Obj.Name));
if Results.RecordCount = 0 then
raise Exception.Create('Can''t find view definition for "'+FEditObjectName+'" in '+DBNAME_INFORMATION_SCHEMA);
raise Exception.Create('Can''t find view definition for "'+Obj.Name+'" in '+DBNAME_INFORMATION_SCHEMA);
// Algorithm is not changeable as we cannot look up its current state!
rgAlgorithm.Enabled := False;
rgAlgorithm.ItemIndex := 0;
@ -127,7 +126,7 @@ procedure TfrmView.btnHelpClick(Sender: TObject);
var
keyword: String;
begin
if FEditObjectName = '' then
if DBObject.Name = '' then
keyword := 'CREATE VIEW'
else
keyword := 'ALTER VIEW';
@ -139,7 +138,7 @@ procedure TfrmView.btnDiscardClick(Sender: TObject);
begin
// Reinit editor, discarding changes
Modified := False;
Init(FEditObjectName);
Init(DBObject);
end;
@ -158,12 +157,12 @@ var
begin
// Save changes
Result := mrOk;
if FEditObjectName = '' then begin
if DBObject.Name = '' then begin
sql := 'CREATE ';
viewname := editName.Text;
end else begin
sql := 'ALTER ';
viewname := FEditObjectName;
viewname := DBObject.Name;
end;
viewname := Mainform.mask(viewname);
if rgAlgorithm.Enabled and (rgAlgorithm.ItemIndex > -1) then
@ -175,13 +174,13 @@ begin
try
Mainform.Connection.Query(sql);
// Probably rename view
if (FEditObjectName <> '') and (FEditObjectName <> editName.Text) then begin
if (DBObject.Name <> '') and (DBObject.Name <> editName.Text) then begin
renamed := Mainform.mask(editName.Text);
Mainform.Connection.Query('RENAME TABLE '+viewname + ' TO '+renamed);
end;
FEditObjectName := editName.Text;
Mainform.SetEditorTabCaption(Self, FEditObjectName);
Mainform.RefreshTreeDB(Mainform.ActiveDatabase, FEditObjectName, lntView);
DBObject.Name := editName.Text;
Mainform.UpdateEditorTab;
Mainform.RefreshTreeDB(Mainform.ActiveDatabase, DBObject.Name, DBObject.NodeType);
Mainform.ParseSelectedTableStructure;
Modified := False;
btnSave.Enabled := Modified;