Implement handling of triggers as separate database objects, on the same level as tables, routines and views. Means we have a trigger editor frame, a new icon and various code extensions. Fixes issue #806.

This commit is contained in:
Ansgar Becker
2009-12-11 16:07:59 +00:00
parent 6b92c3b08c
commit b4cca2ea1f
9 changed files with 480 additions and 14 deletions

View File

@ -35,7 +35,8 @@ uses
table_editor in '..\..\source\table_editor.pas' {frmTableEditor},
mysql_api in '..\..\source\mysql_api.pas',
mysql_connection in '..\..\source\mysql_connection.pas',
cUnicodeCodecs in '..\..\source\cUnicodeCodecs.pas';
cUnicodeCodecs in '..\..\source\cUnicodeCodecs.pas',
trigger_editor in '..\..\source\trigger_editor.pas' {frmTriggerEditor: TFrame};
{$R ..\..\res\icon.RES}
{$R ..\..\res\version.RES}

View File

@ -180,6 +180,9 @@
<DCCReference Include="..\..\source\texteditor.pas">
<Form>frmTextEditor</Form>
</DCCReference>
<DCCReference Include="..\..\source\trigger_editor.pas">
<Form>frmTriggerEditor</Form>
</DCCReference>
<DCCReference Include="..\..\source\updatecheck.pas">
<Form>frmUpdateCheck</Form>
</DCCReference>

BIN
res/icons/cog.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

View File

@ -247,6 +247,7 @@ const
ICONINDEX_CRASHED_TABLE = -1;
ICONINDEX_STOREDPROCEDURE = 119;
ICONINDEX_STOREDFUNCTION = 35;
ICONINDEX_TRIGGER = 137;
ICONINDEX_FUNCTION = 13;
ICONINDEX_KEYWORD = 25;
@ -312,3 +313,5 @@ const
FKEY = 'FULLTEXT';
SKEY = 'SPATIAL';
SYNCOMPLETION_PATTERN: WideString = '\image{%d}\hspace{5}\color{clSilver}%s\column{}\color{clWindowText}%s';

View File

@ -15,7 +15,7 @@ uses Classes, SysUtils, Graphics, GraphUtil, db, clipbrd, dialogs,
type
TListNodeType = (lntNone, lntDb, lntTable, lntCrashedTable, lntView, lntFunction, lntProcedure, lntColumn);
TListNodeType = (lntNone, lntDb, lntTable, lntCrashedTable, lntView, lntFunction, lntProcedure, lntTrigger, lntColumn);
TListNodeTypes = Set of TListNodeType;
TListNode = record
Text: WideString;
@ -2194,7 +2194,9 @@ begin
else if t = 'FUNCTION' then
Result := lntFunction
else if t = 'PROCEDURE' then
Result := lntProcedure;
Result := lntProcedure
else if t = 'TRIGGER' then
Result := lntTrigger;
end else begin
if
TableStatus.IsNull(1) and // Engine column is NULL for views

View File

@ -2241,6 +2241,13 @@ object MainForm: TMainForm
ImageIndex = 19
OnExecute = actTableToolsExecute
end
object actCreateTrigger: TAction
Category = 'Database'
Caption = 'Trigger'
Hint = 'Create a trigger'
ImageIndex = 137
OnExecute = actCreateTriggerExecute
end
end
object SaveDialog2: TSaveDialog
DefaultExt = 'reg'
@ -6039,6 +6046,29 @@ object MainForm: TMainForm
411C2FC06964B5EBFC0F200C40D1D6D566E00000000049454E44AE426082}
Name = 'PngImage136'
Background = clWindow
end
item
PngImage.Data = {
89504E470D0A1A0A0000000D4948445200000010000000100804000000B5FA37
EA0000000467414D410000AFC837058AE90000001974455874536F6674776172
650041646F626520496D616765526561647971C9653C000001C34944415478DA
4D514D48545114FEEEC31C9A37FDBC11A79B4A61D1420884878B84C0160AAE5B
CF4A7117D1225AF8FCC1D171693B0986DC680BB111DCB431988DE84039D4A398
4D1158D230538EDADCF14DF7BE773B3D453D97CB8573BEFB9DEF9C8F699CC5FB
6195F421971EBC3CCBB16380DBE5EFFA3555BAD1AA91AF44B98AC9F687C55380
9BF1875459D9AAD0C1353E97B42D0B32A116922321E0A3139BB1B0879F424539
0BF045ABFA4DB3031FF069EC699A00EFC6FF4E71E6E1020C4804F436E8F858D3
DEE4DC74D862337D65D4A3D40176A8DC892602D6E1CE3E73A8457E5B2594C92D
812A36B7C4440456AAA757C344AE2A852AB30DFF8EF11B7FE86711DF069CB740
A63FBECE8921866B980B58CEBF6DECA386087E6067E01101E6FB5BD6DB207011
713C0FD8E2F6AF8465765B476886BB25272464CAEE95A468A55A13B21C8ACCA6
AF8F2A9266E03B15DAE81E11436E76DE09F7F07A5C4E75B37D6268867732A687
CB58D48DC917FFC77CE5F0995BC8E3AB30A27DB4A815CDEAF7CCBB58853B964D
872D5299BD21518EDB970A835C2153F2ECA0504FC8853723A7663DE992BB576B
91D2FD5681E58AE646ECB07DAD78CECDE3783C5C4936C096B2E7ECFE0748C2CE
3B74ACC3980000000049454E44AE426082}
Name = 'PngImage137'
Background = clWindow
end>
PngOptions = [pngBlendOnDisabled, pngGrayscaleOnDisabled]
Left = 104
@ -6090,6 +6120,9 @@ object MainForm: TMainForm
object menuCreateRoutine: TMenuItem
Action = actCreateRoutine
end
object menuCreateTrigger: TMenuItem
Action = actCreateTrigger
end
end
object N17: TMenuItem
Caption = '-'

View File

@ -20,7 +20,7 @@ uses
SynCompletionProposal, SynEditHighlighter, SynHighlighterSQL,
TntStdCtrls, Tabs, SynUnicode, EditVar, helpers,
createdatabase, table_editor, SynRegExpr,
WideStrUtils, ExtActns, CommCtrl, routine_editor, options,
WideStrUtils, ExtActns, CommCtrl, routine_editor, trigger_editor, options,
Contnrs, PngSpeedButton, connections, SynEditKeyCmds,
mysql_connection, mysql_api, insertfiles, TntComCtrls;
@ -444,6 +444,8 @@ type
menuQueryHelpersGenerateInsert: TMenuItem;
menuQueryHelpersGenerateUpdate: TMenuItem;
menuQueryHelpersGenerateDelete: TMenuItem;
actCreateTrigger: TAction;
menuCreateTrigger: TMenuItem;
procedure refreshMonitorConfig;
procedure loadWindowConfig;
procedure saveWindowConfig;
@ -703,6 +705,7 @@ type
procedure TimerFilterVTTimer(Sender: TObject);
procedure PageControlMainContextPopup(Sender: TObject; MousePos: TPoint; var Handled: Boolean);
procedure menuQueryHelpersGenerateStatementClick(Sender: TObject);
procedure actCreateTriggerExecute(Sender: TObject);
private
ReachedEOT : Boolean;
FDelimiter: String;
@ -751,6 +754,7 @@ type
SelectDBObjectForm: TfrmSelectDBObject;
SQLHelpForm: TfrmSQLhelp;
RoutineEditor: TfrmRoutineEditor;
TriggerEditor: TfrmTriggerEditor;
OptionsForm: Toptionsform;
SessionManager: TConnForm;
AllDatabases, Databases: TWideStringList;
@ -1094,6 +1098,7 @@ begin
FreeAndNil(OptionsForm);
FreeAndNil(SessionManager);
FreeAndNil(TableEditor);
FreeAndNil(TriggerEditor);
FreeAndNil(CreateDatabaseForm);
debug('mem: clearing query and browse data.');
@ -2166,7 +2171,7 @@ end;
procedure TMainForm.actDropObjectsExecute(Sender: TObject);
var
AllCount : Integer;
Tables, Views, Functions, Procedures: TWideStringList;
Tables, Views, Functions, Procedures, Triggers: TWideStringList;
msg, activeDB : WideString;
InDBTree: Boolean;
Act: TAction;
@ -2203,6 +2208,7 @@ begin
Views := TWideStringlist.Create;
Procedures := TWideStringlist.Create;
Functions := TWideStringlist.Create;
Triggers := TWideStringlist.Create;
Act := Sender as TAction;
InDBTree := (Act.ActionComponent is TMenuItem)
@ -2229,6 +2235,7 @@ begin
lntView: Views.Add(SelectedTable.Text);
lntProcedure: Procedures.Add(SelectedTable.Text);
lntFunction: Functions.Add(SelectedTable.Text);
lntTrigger: Triggers.Add(SelectedTable.Text);
end;
end else begin
// Invoked from database tab
@ -2237,12 +2244,13 @@ begin
Views := GetVTCaptions(ListTables, True, 0, [lntView]);
Procedures := GetVTCaptions(ListTables, True, 0, [lntProcedure]);
Functions := GetVTCaptions(ListTables, True, 0, [lntFunction]);
Triggers := GetVTCaptions(ListTables, True, 0, [lntTrigger]);
end;
// Fix actions temporarily enabled for popup menu.
ValidateControls(Sender);
AllCount := Tables.Count + Views.Count + Procedures.Count + Functions.Count;
AllCount := Tables.Count + Views.Count + Procedures.Count + Functions.Count + Triggers.Count;
// Safety stop to avoid firing DROP TABLE without tablenames
if (AllCount = 0) then
@ -2255,6 +2263,7 @@ begin
if Views.Count > 0 then msg := msg + CRLF + 'View(s): ' + ImplodeStr(', ', Views);
if Procedures.Count > 0 then msg := msg + CRLF + 'Procedure(s): ' + ImplodeStr(', ', Procedures);
if Functions.Count > 0 then msg := msg + CRLF + 'Function(s): ' + ImplodeStr(', ', Functions);
if Triggers.Count > 0 then msg := msg + CRLF + 'Trigger(s): ' + ImplodeStr(', ', Triggers);
if MessageDlg(msg, mtConfirmation, [mbok,mbcancel], 0) <> mrok then
Exit;
@ -2264,6 +2273,7 @@ begin
DoDrop('VIEW', Views, True);
DoDrop('PROCEDURE', Procedures, False);
DoDrop('FUNCTION', Functions, False);
DoDrop('TRIGGER', Triggers, False);
// Refresh ListTables + dbtree so the dropped tables are gone:
actRefresh.Execute;
except
@ -3580,6 +3590,30 @@ begin
' FROM '+mask(DBNAME_INFORMATION_SCHEMA)+'.ROUTINES ' +
'WHERE ROUTINE_SCHEMA = '+esc(db));
end;
if Connection.InformationSchemaObjects.IndexOf('TRIGGERS') > -1 then begin
// Stored routines
Unions.Add('SELECT TRIGGER_NAME AS '+mask(DBO_NAME)+
', ''TRIGGER'' AS '+mask(DBO_TYPE)+
', NULL AS '+mask(DBO_ENGINE)+
', NULL AS '+mask(DBO_VERSION)+
', NULL AS '+mask(DBO_ROWFORMAT)+
', NULL AS '+mask(DBO_ROWS)+
', NULL AS '+mask(DBO_AVGROWLEN)+
', NULL AS '+mask(DBO_DATALEN)+
', NULL AS '+mask(DBO_MAXDATALEN)+
', NULL AS '+mask(DBO_INDEXLEN)+
', NULL AS '+mask(DBO_DATAFREE)+
', NULL AS '+mask(DBO_AUTOINC)+
', CREATED AS '+mask(DBO_CREATED)+
', NULL AS '+mask(DBO_UPDATED)+
', NULL AS '+mask(DBO_CHECKED)+
', DATABASE_COLLATION AS '+mask(DBO_COLLATION)+
', NULL AS '+mask(DBO_CHECKSUM)+
', NULL AS '+mask(DBO_CROPTIONS)+
', NULL AS '+mask(DBO_COMMENT)+
' FROM '+mask(DBNAME_INFORMATION_SCHEMA)+'.TRIGGERS ' +
'WHERE TRIGGER_SCHEMA = '+esc(db));
end;
case Unions.Count of
0: ListObjectsSQL := 'SHOW TABLE STATUS FROM ' + mask(db);
1: ListObjectsSQL := Unions[0] + ' ORDER BY `Name`';
@ -3722,6 +3756,7 @@ begin
lntView: img := ICONINDEX_VIEW;
lntProcedure: img := ICONINDEX_STOREDPROCEDURE;
lntFunction: img := ICONINDEX_STOREDFUNCTION;
lntTrigger: img := ICONINDEX_TRIGGER;
else img := -1;
end;
VTRowDataListTables[i].ImageIndex := img;
@ -4061,8 +4096,6 @@ var
Proposal : TSynCompletionProposal;
Editor : TCustomSynEdit;
Queries : TWideStringList;
const
ItemPattern: WideString = '\image{%d}\hspace{5}\color{clSilver}%s\column{}\color{clWindowText}%s';
procedure addTable(Results: TMySQLQuery);
var ObjName, ObjType: WideString; Icon: Integer;
@ -4077,10 +4110,11 @@ const
lntFunction: Icon := ICONINDEX_STOREDFUNCTION;
lntProcedure: Icon := ICONINDEX_STOREDPROCEDURE;
lntView: Icon := ICONINDEX_VIEW;
lntTrigger: Icon := ICONINDEX_TRIGGER;
else Icon := -1;
end;
Proposal.InsertList.Add( ObjName );
Proposal.ItemList.Add( WideFormat(ItemPattern, [Icon, ObjType, ObjName]) );
Proposal.ItemList.Add( WideFormat(SYNCOMPLETION_PATTERN, [Icon, ObjType, ObjName]) );
end;
procedure addColumns( tablename: WideString );
@ -4105,7 +4139,7 @@ const
end;
while not Columns.Eof do begin
Proposal.InsertList.Add(Columns.Col('Field'));
Proposal.ItemList.Add(WideFormat(ItemPattern, [ICONINDEX_FIELD, GetFirstWord(Columns.Col('Type')), Columns.Col('Field')]) );
Proposal.ItemList.Add(WideFormat(SYNCOMPLETION_PATTERN, [ICONINDEX_FIELD, GetFirstWord(Columns.Col('Type')), Columns.Col('Field')]) );
Columns.Next;
end;
FreeAndNil(Columns);
@ -4143,7 +4177,7 @@ begin
Results := Connection.GetResults('SHOW '+UpperCase(rx.Match[1])+' VARIABLES');
while not Results.Eof do begin
Proposal.InsertList.Add(Results.Col(0));
Proposal.ItemList.Add(WideFormat(ItemPattern, [ICONINDEX_PRIMARYKEY, 'variable', Results.Col(0)+' \color{clSilver}= '+WideStringReplace(Results.Col(1), '\', '\\', [rfReplaceAll])] ) );
Proposal.ItemList.Add(WideFormat(SYNCOMPLETION_PATTERN, [ICONINDEX_PRIMARYKEY, 'variable', Results.Col(0)+' \color{clSilver}= '+WideStringReplace(Results.Col(1), '\', '\\', [rfReplaceAll])] ) );
Results.Next;
end;
except
@ -4234,7 +4268,7 @@ begin
// Add databases
for i := 0 to Databases.Count - 1 do begin
Proposal.InsertList.Add(Databases[i]);
Proposal.ItemList.Add(WideFormat(ItemPattern, [ICONINDEX_DB, 'database', Databases[i]]));
Proposal.ItemList.Add(WideFormat(SYNCOMPLETION_PATTERN, [ICONINDEX_DB, 'database', Databases[i]]));
end;
if ActiveDatabase <> '' then begin
@ -4254,13 +4288,13 @@ begin
if MySqlFunctions[i].Version > Connection.ServerVersionInt then
continue;
Proposal.InsertList.Add( MySQLFunctions[i].Name + MySQLFunctions[i].Declaration );
Proposal.ItemList.Add( WideFormat(ItemPattern, [ICONINDEX_FUNCTION, 'function', MySQLFunctions[i].Name + '\color{clSilver}' + MySQLFunctions[i].Declaration] ) );
Proposal.ItemList.Add( WideFormat(SYNCOMPLETION_PATTERN, [ICONINDEX_FUNCTION, 'function', MySQLFunctions[i].Name + '\color{clSilver}' + MySQLFunctions[i].Declaration] ) );
end;
// Add keywords
for i := 0 to MySQLKeywords.Count - 1 do begin
Proposal.InsertList.Add( MySQLKeywords[i] );
Proposal.ItemList.Add( WideFormat(ItemPattern, [ICONINDEX_KEYWORD, 'keyword', MySQLKeywords[i]] ) );
Proposal.ItemList.Add( WideFormat(SYNCOMPLETION_PATTERN, [ICONINDEX_KEYWORD, 'keyword', MySQLKeywords[i]] ) );
end;
end;
@ -6007,6 +6041,8 @@ begin
ImageIndex := ICONINDEX_STOREDPROCEDURE;
lntFunction:
ImageIndex := ICONINDEX_STOREDFUNCTION;
lntTrigger:
ImageIndex := ICONINDEX_TRIGGER;
end;
end;
end;
@ -8049,6 +8085,15 @@ begin
end;
procedure TMainForm.actCreateTriggerExecute(Sender: TObject);
begin
tabEditor.TabVisible := True;
PagecontrolMain.ActivePage := tabEditor;
PlaceObjectEditor(lntTrigger);
TriggerEditor.Init;
end;
procedure TMainForm.DataGridScroll(Sender: TBaseVirtualTree; DeltaX, DeltaY: Integer);
var
query: String;
@ -8117,6 +8162,8 @@ begin
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, lntCrashedTable] then begin
if not Assigned(TableEditor) then
TableEditor := TfrmTableEditor.Create(tabEditor);
@ -8129,6 +8176,10 @@ begin
if not Assigned(RoutineEditor) then
RoutineEditor := TfrmRoutineEditor.Create(tabEditor);
frm := RoutineEditor;
end else if Which = lntTrigger then begin
if not Assigned(TriggerEditor) then
TriggerEditor := TfrmTriggerEditor.Create(tabEditor);
frm := TriggerEditor;
end else
Exit;
frm.Parent := tabEditor;
@ -8149,6 +8200,9 @@ begin
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;
@ -8212,6 +8266,11 @@ begin
RoutineEditor.Init(SelectedTable.Text, RoutineType);
end;
lntTrigger: begin
PlaceObjectEditor(SelectedTable.NodeType);
TriggerEditor.Init(SelectedTable.Text);
end;
end;
end;
@ -8824,6 +8883,8 @@ begin
Editors.Add(ViewEditor.SynMemoSelect);
if Assigned(RoutineEditor) then
Editors.Add(RoutineEditor.SynMemoBody);
if Assigned(TriggerEditor) then
Editors.Add(TriggerEditor.SynMemoStatement);
if Assigned(CreateDatabaseForm) then
Editors.Add(CreateDatabaseForm.SynMemoPreview);
if Assigned(OptionsForm) then
@ -8843,10 +8904,16 @@ begin
Editor.Font.Size := FontSize;
Editor.Gutter.Font.Name := FontName;
Editor.Gutter.Font.Size := FontSize;
Editor.Gutter.AutoSize := BaseEditor.Gutter.AutoSize;
Editor.Gutter.DigitCount := BaseEditor.Gutter.DigitCount;
Editor.Gutter.LeftOffset := BaseEditor.Gutter.LeftOffset;
Editor.Gutter.RightOffset := BaseEditor.Gutter.RightOffset;
Editor.Gutter.ShowLineNumbers := BaseEditor.Gutter.ShowLineNumbers;
Editor.ActiveLineColor := ActiveLineColor;
Editor.Options := BaseEditor.Options;
Editor.TabWidth := TabWidth;
Editor.MaxScrollWidth := BaseEditor.MaxScrollWidth;
Editor.WantTabs := BaseEditor.WantTabs;
// Shortcuts
if Editor = BaseEditor then for j:=0 to Editor.Keystrokes.Count-1 do begin
KeyStroke := Editor.Keystrokes[j];

160
source/trigger_editor.dfm Normal file
View File

@ -0,0 +1,160 @@
object frmTriggerEditor: TfrmTriggerEditor
Left = 0
Top = 0
Width = 477
Height = 332
TabOrder = 0
DesignSize = (
477
332)
object lblName: TLabel
Left = 3
Top = 6
Width = 31
Height = 13
Caption = 'Name:'
FocusControl = editName
end
object lblTable: TLabel
Left = 3
Top = 33
Width = 45
Height = 13
Caption = 'On table:'
end
object lblBody: TLabel
Left = 3
Top = 87
Width = 327
Height = 13
Caption = 'Trigger statement: (e.g. "SET NEW.columnA = TRIM(OLD.columnA")'
end
object lblEvent: TLabel
Left = 3
Top = 59
Width = 32
Height = 13
Caption = 'Event:'
end
object editName: TTntEdit
Left = 96
Top = 3
Width = 378
Height = 21
Anchors = [akLeft, akTop, akRight]
TabOrder = 0
Text = 'editName'
OnChange = Modification
end
object SynMemoStatement: TSynMemo
Left = 3
Top = 106
Width = 471
Height = 192
SingleLineMode = False
Anchors = [akLeft, akTop, akRight, akBottom]
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'Courier New'
Font.Style = []
TabOrder = 2
Gutter.Font.Charset = DEFAULT_CHARSET
Gutter.Font.Color = clWindowText
Gutter.Font.Height = -11
Gutter.Font.Name = 'Courier New'
Gutter.Font.Style = []
Lines.UnicodeStrings = 'SynMemoStatement'
OnChange = Modification
end
object btnHelp: TButton
Left = 3
Top = 304
Width = 75
Height = 25
Anchors = [akLeft, akBottom]
Caption = 'Help'
TabOrder = 3
OnClick = btnHelpClick
end
object btnDiscard: TButton
Left = 84
Top = 304
Width = 75
Height = 25
Anchors = [akLeft, akBottom]
Caption = 'Discard'
TabOrder = 4
OnClick = btnDiscardClick
end
object btnSave: TButton
Left = 165
Top = 304
Width = 75
Height = 25
Anchors = [akLeft, akBottom]
Caption = 'Save'
Default = True
TabOrder = 5
OnClick = btnSaveClick
end
object comboTable: TTntComboBox
Left = 96
Top = 30
Width = 378
Height = 21
Style = csDropDownList
Anchors = [akLeft, akTop, akRight]
ItemHeight = 13
TabOrder = 1
OnChange = Modification
end
object comboTiming: TComboBox
Left = 96
Top = 56
Width = 145
Height = 21
Style = csDropDownList
ItemHeight = 13
TabOrder = 6
OnChange = Modification
end
object comboEvent: TComboBox
Left = 247
Top = 56
Width = 145
Height = 21
Style = csDropDownList
ItemHeight = 13
TabOrder = 7
OnChange = Modification
end
object SynCompletionProposalStatement: TSynCompletionProposal
Options = [scoLimitToMatchedText, scoUseInsertList, scoUsePrettyText, scoEndCharCompletion, scoCompleteWithTab, scoCompleteWithEnter]
EndOfTokenChr = '()[]. '
TriggerChars = '.'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
TitleFont.Charset = DEFAULT_CHARSET
TitleFont.Color = clBtnText
TitleFont.Height = -11
TitleFont.Name = 'MS Sans Serif'
TitleFont.Style = [fsBold]
Columns = <
item
BiggestWord = 'CONSTRUCTOR'
BiggestWordW = 'CONSTRUCTOR'
end>
Images = MainForm.PngImageListMain
OnExecute = SynCompletionProposalStatementExecute
ShortCut = 16416
Editor = SynMemoStatement
Left = 264
Top = 304
EndOfTokenChrW = '()[]. '
TriggerCharsW = '.'
end
end

197
source/trigger_editor.pas Normal file
View File

@ -0,0 +1,197 @@
unit trigger_editor;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, TntStdCtrls, SynEdit, SynMemo, ExtCtrls, mysql_connection, mysql_api,
SynCompletionProposal, VirtualTrees;
type
TfrmTriggerEditor = class(TFrame)
lblName: TLabel;
editName: TTntEdit;
SynMemoStatement: TSynMemo;
btnHelp: TButton;
btnDiscard: TButton;
btnSave: TButton;
comboTable: TTntComboBox;
lblTable: TLabel;
lblBody: TLabel;
SynCompletionProposalStatement: TSynCompletionProposal;
lblEvent: TLabel;
comboTiming: TComboBox;
comboEvent: TComboBox;
procedure btnHelpClick(Sender: TObject);
procedure btnDiscardClick(Sender: TObject);
procedure Modification(Sender: TObject);
procedure btnSaveClick(Sender: TObject);
procedure SynCompletionProposalStatementExecute(Kind: SynCompletionType; Sender: TObject;
var CurrentInput: WideString; var x, y: Integer; var CanExecute: Boolean);
private
{ Private declarations }
FEditTriggerName: WideString;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
procedure Init(EditTriggerName: WideString='');
end;
implementation
uses main, helpers;
{$R *.dfm}
{**
Create: Restore GUI setup
}
constructor TfrmTriggerEditor.Create(AOwner: TComponent);
var
col: TProposalColumn;
i: Integer;
begin
inherited Create(AOwner);
Align := alClient;
SynMemoStatement.Highlighter := Mainform.SynSQLSyn1;
InheritFont(Font);
editName.MaxLength := NAME_LEN;
comboTiming.Items.Text := 'BEFORE'+CRLF+'AFTER';
comboEvent.Items.Text := 'INSERT'+CRLF+'UPDATE'+CRLF+'DELETE';
for i:=0 to Mainform.SynCompletionProposal.Columns.Count-1 do begin
col := SynCompletionProposalStatement.Columns.Add;
col.BiggestWord := Mainform.SynCompletionProposal.Columns[i].BiggestWord;
end;
SynCompletionProposalStatement.NbLinesInWindow := Mainform.SynCompletionProposal.NbLinesInWindow;
SynCompletionProposalStatement.Width := Mainform.SynCompletionProposal.Width;
SynCompletionProposalStatement.Options := Mainform.SynCompletionProposal.Options;
SynCompletionProposalStatement.TimerInterval := Mainform.SynCompletionProposal.TimerInterval;
SynCompletionProposalStatement.ItemHeight := Mainform.SynCompletionProposal.ItemHeight;
SynCompletionProposalStatement.Margin := Mainform.SynCompletionProposal.Margin;
SynCompletionProposalStatement.Font := Font;
end;
procedure TfrmTriggerEditor.Init(EditTriggerName: WideString='');
var
Definition, TableList: TMySQLQuery;
begin
Mainform.SetupSynEditors;
FEditTriggerName := EditTriggerName;
editName.Text := '';
SynMemoStatement.Text := '';
comboEvent.ItemIndex := 0;
comboTiming.ItemIndex := 0;
TableList := Mainform.FetchActiveDbTableList;
comboTable.Items.Clear;
while not TableList.Eof do begin
if GetDBObjectType(TableList) in [lntTable, lntCrashedTable] then
comboTable.Items.Add(TableList.Col(DBO_NAME));
TableList.Next;
end;
if comboTable.Items.Count > 0 then
comboTable.ItemIndex := 0;
if FEditTriggerName <> '' then begin
// Edit mode
Mainform.SetEditorTabCaption(Self, FEditTriggerName);
editName.Text := FEditTriggerName;
Definition := Mainform.Connection.GetResults('SELECT '+
'EVENT_MANIPULATION, EVENT_OBJECT_TABLE, ACTION_STATEMENT, ACTION_TIMING '+
'FROM information_schema.TRIGGERS '+
'WHERE TRIGGER_SCHEMA='+esc(Mainform.ActiveDatabase)+' AND TRIGGER_NAME='+esc(FEditTriggerName));
comboTable.ItemIndex := comboTable.Items.IndexOf(Definition.Col('EVENT_OBJECT_TABLE'));
comboTiming.ItemIndex := comboTiming.Items.IndexOf(UpperCase(Definition.Col('ACTION_TIMING')));
comboEvent.ItemIndex := comboEvent.Items.IndexOf(UpperCase(Definition.Col('EVENT_MANIPULATION')));
SynMemoStatement.Text := Definition.Col('ACTION_STATEMENT');
end else begin
Mainform.SetEditorTabCaption(Self, '');
editName.Text := 'Enter trigger name';
end;
btnSave.Enabled := False;
btnDiscard.Enabled := False;
end;
procedure TfrmTriggerEditor.Modification(Sender: TObject);
begin
// Enable buttons if anything has changed
btnSave.Enabled := (editName.Text <> '') and (comboTable.ItemIndex > -1)
and (comboTiming.ItemIndex > -1) and (comboEvent.ItemIndex > -1)
and (SynMemoStatement.Text <> '');
btnDiscard.Enabled := True;
end;
procedure TfrmTriggerEditor.btnDiscardClick(Sender: TObject);
begin
// Reinit editor, discarding changes
Init(FEditTriggerName);
end;
procedure TfrmTriggerEditor.btnSaveClick(Sender: TObject);
var
sql: WideString;
FocusChangeEvent: procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex) of object;
begin
// Edit mode means we drop the trigger and recreate it, as there is no ALTER TRIGGER.
try
if FEditTriggerName <> '' then
Mainform.Connection.Query('DROP TRIGGER '+Mainform.mask(FEditTriggerName));
// CREATE
// [DEFINER = { user | CURRENT_USER }]
// TRIGGER trigger_name trigger_time trigger_event
// ON tbl_name FOR EACH ROW trigger_stmt
sql := 'CREATE TRIGGER '+Mainform.mask(editName.Text)+' '+
comboTiming.Items[comboTiming.ItemIndex]+' '+comboEvent.Items[comboEvent.ItemIndex]+
' ON '+Mainform.mask(comboTable.Text)+
' FOR EACH ROW '+SynMemoStatement.Text;
Mainform.Connection.Query(sql);
FocusChangeEvent := Mainform.DBtree.OnFocusChanged;
Mainform.DBtree.OnFocusChanged := nil;
Mainform.RefreshTreeDB(Mainform.ActiveDatabase);
Mainform.DBtree.OnFocusChanged := FocusChangeEvent;
Mainform.SelectDBObject(editName.Text, lntTrigger);
Init(editName.Text);
except on E:Exception do
MessageDlg(E.Message, mtError, [mbOK], 0);
end;
end;
procedure TfrmTriggerEditor.SynCompletionProposalStatementExecute(Kind: SynCompletionType; Sender: TObject;
var CurrentInput: WideString; var x, y: Integer; var CanExecute: Boolean);
var
Proposal: TSynCompletionProposal;
Token: WideString;
Columns: TMySQLQuery;
begin
// Propose column names from referencing table
Proposal := Sender as TSynCompletionProposal;
Token := UpperCase(Proposal.PreviousToken);
Proposal.InsertList.Clear;
Proposal.ItemList.Clear;
if (Token = 'NEW') or (Token = 'OLD') then begin
if comboTable.Text = '' then
CanExecute := False
else try
Columns := Mainform.Connection.GetResults('SHOW COLUMNS FROM '+Mainform.mask(comboTable.Text));
while not Columns.Eof do begin
Proposal.InsertList.Add(Columns.Col('Field'));
Proposal.ItemList.Add(WideFormat(SYNCOMPLETION_PATTERN, [ICONINDEX_FIELD, GetFirstWord(Columns.Col('Type')), Columns.Col('Field')]) );
Columns.Next;
end;
except
end;
end else
Mainform.SynCompletionProposalExecute(Kind, Sender, CurrentInput, x, y, CanExecute);
end;
procedure TfrmTriggerEditor.btnHelpClick(Sender: TObject);
begin
Mainform.CallSQLHelpWithKeyword('TRIGGERS');
end;
end.