Implement bulk table editing in table tools dialog. Fixes issue #576

This commit is contained in:
Ansgar Becker
2009-11-28 19:51:57 +00:00
parent a0fc5e2065
commit 30b970c92b
5 changed files with 245 additions and 24 deletions

View File

@ -2231,6 +2231,12 @@ object MainForm: TMainForm
ShortCut = 16454 ShortCut = 16454
OnExecute = actFilterPanelExecute OnExecute = actFilterPanelExecute
end end
object actBulkTableEdit: TAction
Category = 'Tools'
Caption = 'Bulk table editor'
ImageIndex = 19
OnExecute = actTableToolsExecute
end
end end
object SaveDialog2: TSaveDialog object SaveDialog2: TSaveDialog
DefaultExt = 'reg' DefaultExt = 'reg'
@ -6093,6 +6099,9 @@ object MainForm: TMainForm
object Findtextonserver1: TMenuItem object Findtextonserver1: TMenuItem
Action = actFindTextOnServer Action = actFindTextOnServer
end end
object menuBulkTableEdit: TMenuItem
Action = actBulkTableEdit
end
object menuEmptyTables: TMenuItem object menuEmptyTables: TMenuItem
Action = actEmptyTables Action = actEmptyTables
end end

View File

@ -439,6 +439,8 @@ type
actFindTextOnServer: TAction; actFindTextOnServer: TAction;
actFindTextOnServer1: TMenuItem; actFindTextOnServer1: TMenuItem;
Findtextonserver1: TMenuItem; Findtextonserver1: TMenuItem;
actBulkTableEdit: TAction;
menuBulkTableEdit: TMenuItem;
procedure refreshMonitorConfig; procedure refreshMonitorConfig;
procedure loadWindowConfig; procedure loadWindowConfig;
procedure saveWindowConfig; procedure saveWindowConfig;
@ -1798,7 +1800,9 @@ begin
else if Sender = actFindTextOnServer then else if Sender = actFindTextOnServer then
TableToolsDialog.ToolMode := tmFind TableToolsDialog.ToolMode := tmFind
else if Sender = actExportTables then else if Sender = actExportTables then
TableToolsDialog.ToolMode := tmSQLExport; TableToolsDialog.ToolMode := tmSQLExport
else if Sender = actBulkTableEdit then
TableToolsDialog.ToolMode := tmBulkTableEdit;
TableToolsDialog.ShowModal; TableToolsDialog.ShowModal;
end; end;

View File

@ -87,7 +87,7 @@ type
FTableEngines: TStringList; FTableEngines: TStringList;
FTableEngineDefault: String; FTableEngineDefault: String;
FCollationTable: TMySQLQuery; FCollationTable: TMySQLQuery;
FCollationsUnavailable: Boolean; FCharsetTable: TMySQLQuery;
procedure SetActive(Value: Boolean); procedure SetActive(Value: Boolean);
procedure SetDatabase(Value: WideString); procedure SetDatabase(Value: WideString);
function GetThreadId: Cardinal; function GetThreadId: Cardinal;
@ -99,6 +99,8 @@ type
function GetTableEngines: TStringList; function GetTableEngines: TStringList;
function GetCollationTable: TMySQLQuery; function GetCollationTable: TMySQLQuery;
function GetCollationList: TStringList; function GetCollationList: TStringList;
function GetCharsetTable: TMySQLQuery;
function GetCharsetList: TStringList;
function GetConnectionUptime: Integer; function GetConnectionUptime: Integer;
function GetServerUptime: Integer; function GetServerUptime: Integer;
procedure Log(Category: TMySQLLogCategory; Msg: WideString); procedure Log(Category: TMySQLLogCategory; Msg: WideString);
@ -135,6 +137,8 @@ type
property TableEngineDefault: String read FTableEngineDefault; property TableEngineDefault: String read FTableEngineDefault;
property CollationTable: TMySQLQuery read GetCollationTable; property CollationTable: TMySQLQuery read GetCollationTable;
property CollationList: TStringList read GetCollationList; property CollationList: TStringList read GetCollationList;
property CharsetTable: TMySQLQuery read GetCharsetTable;
property CharsetList: TStringList read GetCharsetList;
published published
property Active: Boolean read FActive write SetActive default False; property Active: Boolean read FActive write SetActive default False;
property Hostname: String read FHostname write FHostname; property Hostname: String read FHostname write FHostname;
@ -687,12 +691,8 @@ end;
function TMySQLConnection.GetCollationTable: TMySQLQuery; function TMySQLConnection.GetCollationTable: TMySQLQuery;
begin begin
if (not Assigned(FCollationTable)) and (not FCollationsUnavailable) then try if (not Assigned(FCollationTable)) and (ServerVersionInt >= 40100) then
FCollationTable := GetResults('SHOW COLLATION'); FCollationTable := GetResults('SHOW COLLATION');
except
// Ignore errors on old servers
FCollationsUnavailable := True;
end;
if Assigned(FCollationTable) then if Assigned(FCollationTable) then
FCollationTable.First; FCollationTable.First;
Result := FCollationTable; Result := FCollationTable;
@ -705,13 +705,36 @@ var
begin begin
c := CollationTable; c := CollationTable;
Result := TStringList.Create; Result := TStringList.Create;
if not FCollationsUnavailable then while not c.Eof do begin if Assigned(c) then while not c.Eof do begin
Result.Add(c.Col('Collation')); Result.Add(c.Col('Collation'));
c.Next; c.Next;
end; end;
end; end;
function TMySQLConnection.GetCharsetTable: TMySQLQuery;
begin
if (not Assigned(FCharsetTable)) and (ServerVersionInt >= 40100) then
FCharsetTable := GetResults('SHOW CHARSET');
if Assigned(FCharsetTable) then
FCharsetTable.First;
Result := FCharsetTable;
end;
function TMySQLConnection.GetCharsetList: TStringList;
var
c: TMySQLQuery;
begin
c := CharsetTable;
Result := TStringList.Create;
if Assigned(c) then while not c.Eof do begin
Result.Add(c.Col('Description') + ' (' + c.Col('Charset') + ')');
c.Next;
end;
end;
function TMySQLConnection.GetConnectionUptime: Integer; function TMySQLConnection.GetConnectionUptime: Integer;
begin begin
// Return seconds since last connect // Return seconds since last connect
@ -733,7 +756,7 @@ procedure TMySQLConnection.ClearCache;
begin begin
// Free cached lists and results. Called when the connection was closed and/or destroyed // Free cached lists and results. Called when the connection was closed and/or destroyed
FreeAndNil(FCollationTable); FreeAndNil(FCollationTable);
FCollationsUnavailable := False; FreeAndNil(FCharsetTable);
FreeAndNil(FTableEngines); FreeAndNil(FTableEngines);
FTableEngineDefault := ''; FTableEngineDefault := '';
end; end;

View File

@ -117,9 +117,9 @@ object frmTableTools: TfrmTableTools
TabOrder = 1 TabOrder = 1
object ResultGrid: TVirtualStringTree object ResultGrid: TVirtualStringTree
Left = 0 Left = 0
Top = 137 Top = 145
Width = 380 Width = 380
Height = 199 Height = 191
Align = alClient Align = alClient
Header.AutoSizeIndex = -1 Header.AutoSizeIndex = -1
Header.DefaultHeight = 17 Header.DefaultHeight = 17
@ -148,7 +148,7 @@ object frmTableTools: TfrmTableTools
Left = 0 Left = 0
Top = 0 Top = 0
Width = 380 Width = 380
Height = 137 Height = 145
ActivePage = tabSQLexport ActivePage = tabSQLexport
Align = alTop Align = alTop
Images = MainForm.PngImageListMain Images = MainForm.PngImageListMain
@ -159,7 +159,7 @@ object frmTableTools: TfrmTableTools
ImageIndex = 39 ImageIndex = 39
DesignSize = ( DesignSize = (
372 372
108) 116)
object lblOperation: TLabel object lblOperation: TLabel
Left = 3 Left = 3
Top = 14 Top = 14
@ -263,7 +263,7 @@ object frmTableTools: TfrmTableTools
ImageIndex = 30 ImageIndex = 30
DesignSize = ( DesignSize = (
372 372
108) 116)
object lblFindText: TLabel object lblFindText: TLabel
Left = 3 Left = 3
Top = 14 Top = 14
@ -273,16 +273,17 @@ object frmTableTools: TfrmTableTools
end end
object lblDataTypes: TLabel object lblDataTypes: TLabel
Left = 80 Left = 80
Top = 77 Top = 93
Width = 151 Width = 151
Height = 13 Height = 13
Anchors = [akLeft, akBottom]
Caption = 'Restrict search to column types' Caption = 'Restrict search to column types'
end end
object memoFindText: TTntMemo object memoFindText: TTntMemo
Left = 80 Left = 80
Top = 11 Top = 11
Width = 289 Width = 289
Height = 53 Height = 73
Anchors = [akLeft, akTop, akRight, akBottom] Anchors = [akLeft, akTop, akRight, akBottom]
ScrollBars = ssVertical ScrollBars = ssVertical
TabOrder = 0 TabOrder = 0
@ -290,11 +291,11 @@ object frmTableTools: TfrmTableTools
end end
object comboDataTypes: TComboBox object comboDataTypes: TComboBox
Left = 264 Left = 264
Top = 74 Top = 90
Width = 105 Width = 105
Height = 21 Height = 21
Style = csDropDownList Style = csDropDownList
Anchors = [akLeft, akTop, akRight] Anchors = [akLeft, akRight, akBottom]
ItemHeight = 13 ItemHeight = 13
TabOrder = 1 TabOrder = 1
end end
@ -304,7 +305,7 @@ object frmTableTools: TfrmTableTools
ImageIndex = 9 ImageIndex = 9
DesignSize = ( DesignSize = (
372 372
108) 116)
object lblExportData: TLabel object lblExportData: TLabel
Left = 224 Left = 224
Top = 4 Top = 4
@ -419,6 +420,101 @@ object frmTableTools: TfrmTableTools
OnExit = comboExportOutputTargetExit OnExit = comboExportOutputTargetExit
end end
end end
object tabBulkTableEdit: TTabSheet
Caption = 'Bulk table editor'
ImageIndex = 19
DesignSize = (
372
116)
object chkBulkTableEditDatabase: TCheckBox
Left = 3
Top = 5
Width = 135
Height = 17
Caption = 'Move to database:'
TabOrder = 0
OnClick = chkBulkTableEditCheckComboClick
end
object comboBulkTableEditDatabase: TTntComboBox
Left = 168
Top = 3
Width = 200
Height = 21
Style = csDropDownList
Anchors = [akLeft, akTop, akRight]
Enabled = False
ItemHeight = 13
TabOrder = 1
end
object chkBulkTableEditResetAutoinc: TCheckBox
Left = 3
Top = 97
Width = 166
Height = 17
Caption = 'Reset auto increment value'
TabOrder = 2
end
object chkBulkTableEditCollation: TCheckBox
Left = 3
Top = 51
Width = 150
Height = 17
Caption = 'Change default collation:'
TabOrder = 3
OnClick = chkBulkTableEditCheckComboClick
end
object comboBulkTableEditCollation: TComboBox
Left = 168
Top = 49
Width = 200
Height = 21
Style = csDropDownList
Anchors = [akLeft, akTop, akRight]
Enabled = False
ItemHeight = 13
TabOrder = 4
end
object chkBulkTableEditEngine: TCheckBox
Left = 3
Top = 28
Width = 150
Height = 17
Caption = 'Change table engine:'
TabOrder = 5
OnClick = chkBulkTableEditCheckComboClick
end
object comboBulkTableEditEngine: TComboBox
Left = 168
Top = 26
Width = 200
Height = 21
Style = csDropDownList
Anchors = [akLeft, akTop, akRight]
Enabled = False
ItemHeight = 13
TabOrder = 6
end
object chkBulkTableEditCharset: TCheckBox
Left = 3
Top = 74
Width = 150
Height = 17
Caption = 'Convert to charset:'
TabOrder = 7
OnClick = chkBulkTableEditCheckComboClick
end
object comboBulkTableEditCharset: TComboBox
Left = 168
Top = 72
Width = 200
Height = 21
Style = csDropDownList
Anchors = [akLeft, akTop, akRight]
Enabled = False
ItemHeight = 13
TabOrder = 8
end
end
end end
end end
end end

View File

@ -14,7 +14,7 @@ uses
PngSpeedButton, helpers; PngSpeedButton, helpers;
type type
TToolMode = (tmMaintenance, tmFind, tmSQLExport); TToolMode = (tmMaintenance, tmFind, tmSQLExport, tmBulkTableEdit);
TfrmTableTools = class(TForm) TfrmTableTools = class(TForm)
btnClose: TButton; btnClose: TButton;
pnlTop: TPanel; pnlTop: TPanel;
@ -58,6 +58,16 @@ type
lblSkipLargeTablesMB: TLabel; lblSkipLargeTablesMB: TLabel;
lblSkipLargeTables: TLabel; lblSkipLargeTables: TLabel;
btnExportOutputTargetSelect: TPngSpeedButton; btnExportOutputTargetSelect: TPngSpeedButton;
tabBulkTableEdit: TTabSheet;
chkBulkTableEditDatabase: TCheckBox;
comboBulkTableEditDatabase: TTntComboBox;
chkBulkTableEditResetAutoinc: TCheckBox;
chkBulkTableEditCollation: TCheckBox;
comboBulkTableEditCollation: TComboBox;
chkBulkTableEditEngine: TCheckBox;
comboBulkTableEditEngine: TComboBox;
chkBulkTableEditCharset: TCheckBox;
comboBulkTableEditCharset: TComboBox;
procedure FormDestroy(Sender: TObject); procedure FormDestroy(Sender: TObject);
procedure FormCreate(Sender: TObject); procedure FormCreate(Sender: TObject);
procedure FormShow(Sender: TObject); procedure FormShow(Sender: TObject);
@ -89,6 +99,7 @@ type
procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure TreeObjectsPaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; Node: PVirtualNode; procedure TreeObjectsPaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; Node: PVirtualNode;
Column: TColumnIndex; TextType: TVSTTextType); Column: TColumnIndex; TextType: TVSTTextType);
procedure chkBulkTableEditCheckComboClick(Sender: TObject);
private private
{ Private declarations } { Private declarations }
FResults: TObjectList; FResults: TObjectList;
@ -105,6 +116,7 @@ type
procedure DoMaintenance(db, obj: WideString; NodeType: TListNodeType); procedure DoMaintenance(db, obj: WideString; NodeType: TListNodeType);
procedure DoFind(db, obj: WideString; NodeType: TListNodeType; RowsInTable: Int64); procedure DoFind(db, obj: WideString; NodeType: TListNodeType; RowsInTable: Int64);
procedure DoExport(db, obj: WideString; NodeType: TListNodeType; RowsInTable, AvgRowLen: Int64); procedure DoExport(db, obj: WideString; NodeType: TListNodeType; RowsInTable, AvgRowLen: Int64);
procedure DoBulkTableEdit(db, obj: WideString; NodeType: TListNodeType);
public public
{ Public declarations } { Public declarations }
SelectedTables: TWideStringList; SelectedTables: TWideStringList;
@ -251,6 +263,22 @@ begin
comboOperation.Items[comboOperation.Items.IndexOf('Checksum')] := 'Checksum ('+STR_NOTSUPPORTED+')'; comboOperation.Items[comboOperation.Items.IndexOf('Checksum')] := 'Checksum ('+STR_NOTSUPPORTED+')';
comboOperation.OnChange(Sender); comboOperation.OnChange(Sender);
comboExportOutputType.OnChange(Sender); comboExportOutputType.OnChange(Sender);
comboBulkTableEditDatabase.Items.Text := Mainform.Databases.Text;
if comboBulkTableEditDatabase.Items.Count > 0 then
comboBulkTableEditDatabase.ItemIndex := 0;
comboBulkTableEditEngine.Items := Mainform.Connection.TableEngines;
if comboBulkTableEditEngine.Items.Count > 0 then
comboBulkTableEditEngine.ItemIndex := comboBulkTableEditEngine.Items.IndexOf(Mainform.Connection.TableEngineDefault);
comboBulkTableEditCollation.Items := Mainform.Connection.CollationList;
if comboBulkTableEditCollation.Items.Count > 0 then
comboBulkTableEditCollation.ItemIndex := 0;
comboBulkTableEditCharset.Items := Mainform.Connection.CharsetList;
if comboBulkTableEditCharset.Items.Count > 0 then
comboBulkTableEditCharset.ItemIndex := 0;
end; end;
@ -264,7 +292,7 @@ end;
procedure TfrmTableTools.ValidateControls(Sender: TObject); procedure TfrmTableTools.ValidateControls(Sender: TObject);
var var
SomeChecked: Boolean; SomeChecked, OptionChecked: Boolean;
op: String; op: String;
begin begin
SomeChecked := TreeObjects.CheckedCount > 0; SomeChecked := TreeObjects.CheckedCount > 0;
@ -290,6 +318,13 @@ begin
end else if tabsTools.ActivePage = tabSQLExport then begin end else if tabsTools.ActivePage = tabSQLExport then begin
btnExecute.Caption := 'Export'; btnExecute.Caption := 'Export';
btnExecute.Enabled := SomeChecked; btnExecute.Enabled := SomeChecked;
end else if tabsTools.ActivePage = tabBulkTableEdit then begin
btnExecute.Caption := 'Update';
chkBulkTableEditCollation.Enabled := Mainform.Connection.IsUnicode;
chkBulkTableEditCharset.Enabled := Mainform.Connection.IsUnicode;
OptionChecked := chkBulkTableEditDatabase.Checked or chkBulkTableEditEngine.Checked or chkBulkTableEditCollation.Checked
or chkBulkTableEditCharset.Checked or chkBulkTableEditResetAutoinc.Checked;
btnExecute.Enabled := SomeChecked and OptionChecked;
end; end;
end; end;
@ -372,7 +407,9 @@ begin
else if tabsTools.ActivePage = tabFind then else if tabsTools.ActivePage = tabFind then
FToolMode := tmFind FToolMode := tmFind
else if tabsTools.ActivePage = tabSQLExport then else if tabsTools.ActivePage = tabSQLExport then
FToolMode := tmSQLExport; FToolMode := tmSQLExport
else if tabsTools.ActivePage = tabBulkTableEdit then
FToolMode := tmBulkTableEdit;
ResultGrid.Clear; ResultGrid.Clear;
FResults.Clear; FResults.Clear;
TreeObjects.SetFocus; TreeObjects.SetFocus;
@ -393,8 +430,8 @@ begin
AvgRowLen := MakeInt(Results.Col(DBO_AVGROWLEN)); AvgRowLen := MakeInt(Results.Col(DBO_AVGROWLEN));
if (udSkipLargeTables.Position = 0) or ((TableSize div SIZE_MB) < udSkipLargeTables.Position) then try if (udSkipLargeTables.Position = 0) or ((TableSize div SIZE_MB) < udSkipLargeTables.Position) then try
case FToolMode of case FToolMode of
tmMaintenance: DoMaintenance(db, table, NodeType); tmMaintenance: DoMaintenance(db, table, NodeType);
tmFind: DoFind(db, table, NodeType, RowsInTable); tmFind: DoFind(db, table, NodeType, RowsInTable);
tmSQLExport: begin tmSQLExport: begin
// Views have to be exported at the very end so at least all needed tables are ready when a view gets imported // Views have to be exported at the very end so at least all needed tables are ready when a view gets imported
if NodeType = lntView then begin if NodeType = lntView then begin
@ -403,6 +440,7 @@ begin
end else end else
DoExport(db, table, NodeType, RowsInTable, AvgRowLen); DoExport(db, table, NodeType, RowsInTable, AvgRowLen);
end; end;
tmBulkTableEdit: DoBulkTableEdit(db, table, NodeType);
end; end;
except except
// The above SQL can easily throw an exception, e.g. if a table is corrupted. // The above SQL can easily throw an exception, e.g. if a table is corrupted.
@ -747,6 +785,7 @@ begin
chkExportTablesDrop.Checked := False; chkExportTablesDrop.Checked := False;
end; end;
procedure TfrmTableTools.btnExportOutputTargetSelectClick(Sender: TObject); procedure TfrmTableTools.btnExportOutputTargetSelectClick(Sender: TObject);
var var
SaveDialog: TSaveDialog; SaveDialog: TSaveDialog;
@ -783,6 +822,7 @@ begin
tmMaintenance: tabsTools.ActivePage := tabMaintenance; tmMaintenance: tabsTools.ActivePage := tabMaintenance;
tmFind: tabsTools.ActivePage := tabFind; tmFind: tabsTools.ActivePage := tabFind;
tmSQLExport: tabsTools.ActivePage := tabSQLExport; tmSQLExport: tabsTools.ActivePage := tabSQLExport;
tmBulkTableEdit: tabsTools.ActivePage := tabBulkTableEdit;
end; end;
end; end;
@ -1031,4 +1071,53 @@ begin
end; end;
procedure TfrmTableTools.chkBulkTableEditCheckComboClick(Sender: TObject);
var
chk: TCheckBox;
combo: TWinControl;
begin
chk := TCheckBox(Sender);
if chk = chkBulkTableEditDatabase then combo := comboBulkTableEditDatabase
else if chk = chkBulkTableEditEngine then combo := comboBulkTableEditEngine
else if chk = chkBulkTableEditCollation then combo := comboBulkTableEditCollation
else combo := comboBulkTableEditCharset;
combo.Enabled := chk.Checked;
ValidateControls(Sender);
end;
procedure TfrmTableTools.DoBulkTableEdit(db, obj: WideString; NodeType: TListNodeType);
var
Specs, LogRow: TWideStringList;
begin
Specs := TWideStringlist.Create;
if chkBulkTableEditDatabase.Checked and (comboBulkTableEditDatabase.Text <> db) then
Specs.Add('RENAME ' + Mainform.mask(comboBulkTableEditDatabase.Text)+'.'+Mainform.mask(obj));
if chkBulkTableEditEngine.Checked then begin
if Mainform.Connection.ServerVersionInt < 40018 then
Specs.Add('TYPE '+comboBulkTableEditEngine.Text)
else
Specs.Add('ENGINE '+comboBulkTableEditEngine.Text);
end;
if chkBulkTableEditCollation.Checked and (comboBulkTableEditCollation.ItemIndex > -1) then
Specs.Add('COLLATE '+comboBulkTableEditCollation.Text);
if chkBulkTableEditCharset.Checked and (comboBulkTableEditCharset.ItemIndex > -1) then begin
Mainform.Connection.CharsetTable.RecNo := comboBulkTableEditCharset.ItemIndex;
Specs.Add('CONVERT TO CHARSET '+Mainform.Connection.CharsetTable.Col('Charset'));
end;
if chkBulkTableEditResetAutoinc.Checked then
Specs.Add('AUTO_INCREMENT=0');
AddResults('SELECT '+esc(db)+' AS '+Mainform.mask('Database')+', ' +
esc(obj)+' AS '+Mainform.mask('Table')+', ' +
esc('Updating...')+' AS '+Mainform.mask('Operation')+', '+
''''' AS '+Mainform.mask('Result')
);
Mainform.Connection.Query('ALTER TABLE ' + Mainform.mask(db) + '.' + Mainform.mask(obj) + ' ' + ImplodeStr(', ', Specs));
LogRow := TWideStringList(FResults.Last);
LogRow[2] := 'Done';
LogRow[3] := 'Success';
UpdateResultGrid;
end;
end. end.