mirror of
https://github.com/HeidiSQL/HeidiSQL.git
synced 2026-03-13 09:24:25 +08:00
Issue #1289: Add some basic code to the "analyze" button, which detects missing tables in target database.
This commit is contained in:
@@ -2,9 +2,11 @@ object frmSyncDB: TfrmSyncDB
|
||||
Left = 0
|
||||
Top = 0
|
||||
Caption = 'frmSyncDB'
|
||||
ClientHeight = 373
|
||||
ClientWidth = 539
|
||||
ClientHeight = 362
|
||||
ClientWidth = 534
|
||||
Color = clBtnFace
|
||||
Constraints.MinHeight = 400
|
||||
Constraints.MinWidth = 550
|
||||
Font.Charset = DEFAULT_CHARSET
|
||||
Font.Color = clWindowText
|
||||
Font.Height = -11
|
||||
@@ -15,8 +17,8 @@ object frmSyncDB: TfrmSyncDB
|
||||
OnClose = FormClose
|
||||
OnCreate = FormCreate
|
||||
DesignSize = (
|
||||
539
|
||||
373)
|
||||
534
|
||||
362)
|
||||
PixelsPerInch = 96
|
||||
TextHeight = 13
|
||||
object lblSource: TLabel
|
||||
@@ -37,7 +39,7 @@ object frmSyncDB: TfrmSyncDB
|
||||
Left = 8
|
||||
Top = 24
|
||||
Width = 193
|
||||
Height = 310
|
||||
Height = 299
|
||||
AccessibleName = 'tree'
|
||||
Anchors = [akLeft, akTop, akBottom]
|
||||
Header.AutoSizeIndex = 0
|
||||
@@ -64,20 +66,20 @@ object frmSyncDB: TfrmSyncDB
|
||||
Columns = <
|
||||
item
|
||||
Position = 0
|
||||
Width = 193
|
||||
Width = 189
|
||||
WideText = 'Name'
|
||||
end>
|
||||
end
|
||||
object grpTarget: TGroupBox
|
||||
Left = 207
|
||||
Top = 8
|
||||
Width = 324
|
||||
Width = 319
|
||||
Height = 105
|
||||
Anchors = [akLeft, akTop, akRight]
|
||||
Caption = 'Target database or table'
|
||||
TabOrder = 1
|
||||
DesignSize = (
|
||||
324
|
||||
319
|
||||
105)
|
||||
object lblTargetServer: TLabel
|
||||
Left = 9
|
||||
@@ -103,7 +105,7 @@ object frmSyncDB: TfrmSyncDB
|
||||
object comboTargetServer: TComboBox
|
||||
Left = 88
|
||||
Top = 18
|
||||
Width = 228
|
||||
Width = 223
|
||||
Height = 21
|
||||
Style = csDropDownList
|
||||
Anchors = [akLeft, akTop, akRight]
|
||||
@@ -113,7 +115,7 @@ object frmSyncDB: TfrmSyncDB
|
||||
object comboTargetDatabase: TComboBox
|
||||
Left = 88
|
||||
Top = 45
|
||||
Width = 228
|
||||
Width = 223
|
||||
Height = 21
|
||||
Style = csDropDownList
|
||||
Anchors = [akLeft, akTop, akRight]
|
||||
@@ -123,7 +125,7 @@ object frmSyncDB: TfrmSyncDB
|
||||
object comboTargetTable: TComboBox
|
||||
Left = 88
|
||||
Top = 72
|
||||
Width = 228
|
||||
Width = 223
|
||||
Height = 21
|
||||
Style = csDropDownList
|
||||
Anchors = [akLeft, akTop, akRight]
|
||||
@@ -131,8 +133,8 @@ object frmSyncDB: TfrmSyncDB
|
||||
end
|
||||
end
|
||||
object btnClose: TButton
|
||||
Left = 456
|
||||
Top = 340
|
||||
Left = 451
|
||||
Top = 329
|
||||
Width = 75
|
||||
Height = 25
|
||||
Anchors = [akRight, akBottom]
|
||||
@@ -144,8 +146,8 @@ object frmSyncDB: TfrmSyncDB
|
||||
TabOrder = 2
|
||||
end
|
||||
object btnApply: TButton
|
||||
Left = 375
|
||||
Top = 340
|
||||
Left = 370
|
||||
Top = 329
|
||||
Width = 75
|
||||
Height = 25
|
||||
Anchors = [akRight, akBottom]
|
||||
@@ -154,10 +156,11 @@ object frmSyncDB: TfrmSyncDB
|
||||
ImageIndex = 120
|
||||
Images = MainForm.ImageListMain
|
||||
TabOrder = 3
|
||||
OnClick = btnApplyClick
|
||||
end
|
||||
object btnAnalyze: TButton
|
||||
Left = 294
|
||||
Top = 340
|
||||
Left = 289
|
||||
Top = 329
|
||||
Width = 75
|
||||
Height = 25
|
||||
Anchors = [akRight, akBottom]
|
||||
@@ -165,11 +168,12 @@ object frmSyncDB: TfrmSyncDB
|
||||
ImageIndex = 146
|
||||
Images = MainForm.ImageListMain
|
||||
TabOrder = 4
|
||||
OnClick = btnAnalyzeClick
|
||||
end
|
||||
object grpOptions: TGroupBox
|
||||
Left = 207
|
||||
Top = 119
|
||||
Width = 324
|
||||
Width = 319
|
||||
Height = 66
|
||||
Anchors = [akLeft, akTop, akRight]
|
||||
Caption = 'Options'
|
||||
@@ -198,8 +202,8 @@ object frmSyncDB: TfrmSyncDB
|
||||
object treeDifferences: TVirtualStringTree
|
||||
Left = 207
|
||||
Top = 208
|
||||
Width = 324
|
||||
Height = 126
|
||||
Width = 319
|
||||
Height = 115
|
||||
Anchors = [akLeft, akTop, akRight, akBottom]
|
||||
Header.AutoSizeIndex = 0
|
||||
Header.DefaultHeight = 17
|
||||
@@ -211,6 +215,13 @@ object frmSyncDB: TfrmSyncDB
|
||||
Header.MainColumn = -1
|
||||
Images = MainForm.ImageListMain
|
||||
TabOrder = 6
|
||||
TreeOptions.MiscOptions = [toAcceptOLEDrop, toCheckSupport, toFullRepaintOnResize, toInitOnSave, toToggleOnDblClick, toWheelPanning, toEditOnClick]
|
||||
TreeOptions.PaintOptions = [toShowButtons, toShowDropmark, toShowRoot, toShowTreeLines, toThemeAware, toUseBlendedImages, toGhostedIfUnfocused, toUseExplorerTheme, toHideTreeLinesIfThemed]
|
||||
OnGetText = treeDifferencesGetText
|
||||
OnGetImageIndex = treeDifferencesGetImageIndex
|
||||
OnGetNodeDataSize = treeDifferencesGetNodeDataSize
|
||||
OnInitChildren = treeDifferencesInitChildren
|
||||
OnInitNode = treeDifferencesInitNode
|
||||
Columns = <>
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,7 +4,8 @@ interface
|
||||
|
||||
uses
|
||||
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
|
||||
Dialogs, StdCtrls, VirtualTrees;
|
||||
Dialogs, StdCtrls, Generics.Collections, VirtualTrees,
|
||||
dbconnection;
|
||||
|
||||
type
|
||||
TfrmSyncDB = class(TForm)
|
||||
@@ -38,20 +39,61 @@ type
|
||||
procedure treeSourcePaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType);
|
||||
procedure comboTargetServerChange(Sender: TObject);
|
||||
procedure comboTargetDatabaseChange(Sender: TObject);
|
||||
procedure btnAnalyzeClick(Sender: TObject);
|
||||
procedure btnApplyClick(Sender: TObject);
|
||||
procedure treeDifferencesGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer);
|
||||
procedure treeDifferencesInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
|
||||
procedure treeDifferencesGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
|
||||
procedure treeDifferencesInitChildren(Sender: TBaseVirtualTree; Node: PVirtualNode; var ChildCount: Cardinal);
|
||||
procedure treeDifferencesGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer);
|
||||
private
|
||||
{ Private declarations }
|
||||
function CreateTargetConnection: TDBConnection;
|
||||
public
|
||||
{ Public declarations }
|
||||
end;
|
||||
|
||||
TDiffType = (diCreate, diAlter, diInsert);
|
||||
TDiffItem = class(TObject)
|
||||
private
|
||||
FTitle: String;
|
||||
FSQL: String;
|
||||
FDiffType: TDiffType;
|
||||
public
|
||||
property Title: String read FTitle write FTitle;
|
||||
property SQL: String read FSQL write FSQL;
|
||||
property DiffType: TDiffType read FDiffType write FDiffType;
|
||||
end;
|
||||
TDiffObject = class(TObjectList<TDiffItem>)
|
||||
private
|
||||
FDBObject: TDBObject;
|
||||
public
|
||||
function AddItem(Title, SQL: String; DiffType: TDiffType): TDiffItem;
|
||||
property DBObject: TDBObject read FDBObject write FDBObject;
|
||||
end;
|
||||
PDiffObject = ^TDiffObject;
|
||||
|
||||
implementation
|
||||
|
||||
uses main, helpers, dbconnection;
|
||||
uses main, helpers;
|
||||
|
||||
{$R *.dfm}
|
||||
|
||||
|
||||
{ TDiffObject }
|
||||
|
||||
function TDiffObject.AddItem(Title, SQL: String; DiffType: TDiffType): TDiffItem;
|
||||
begin
|
||||
// Add ALTER, INSERT or whatever difference to diff object
|
||||
Result := TDiffItem.Create;
|
||||
Result.Title := Title;
|
||||
Result.SQL := SQL;
|
||||
Result.DiffType := DiffType;
|
||||
Add(Result);
|
||||
end;
|
||||
|
||||
|
||||
{ TfrmSyncDB }
|
||||
|
||||
procedure TfrmSyncDB.FormClose(Sender: TObject; var Action: TCloseAction);
|
||||
begin
|
||||
@@ -63,6 +105,7 @@ procedure TfrmSyncDB.FormCreate(Sender: TObject);
|
||||
var
|
||||
SessNode: PVirtualNode;
|
||||
begin
|
||||
Caption := MainForm.actSynchronizeDatabase.Caption;
|
||||
InheritFont(Font);
|
||||
SetWindowSizeGrip(Self.Handle, True);
|
||||
FixVT(treeSource);
|
||||
@@ -84,7 +127,6 @@ end;
|
||||
|
||||
procedure TfrmSyncDB.comboTargetServerChange(Sender: TObject);
|
||||
var
|
||||
Parameters: TConnectionParameters;
|
||||
Connection: TDBConnection;
|
||||
SessionSelected: Boolean;
|
||||
begin
|
||||
@@ -100,11 +142,7 @@ begin
|
||||
grpOptions.Enabled := SessionSelected;
|
||||
btnAnalyze.Enabled := SessionSelected;
|
||||
if lblTargetDatabase.Enabled then begin
|
||||
Parameters := LoadConnectionParams(comboTargetServer.Text);
|
||||
Connection := Parameters.CreateConnection(Self);
|
||||
Connection.OnLog := MainForm.LogSQL;
|
||||
Connection.LogPrefix := comboTargetServer.Text;
|
||||
Connection.Active := True;
|
||||
Connection := CreateTargetConnection;
|
||||
comboTargetDatabase.Items.Assign(Connection.AllDatabases);
|
||||
Connection.Active := False;
|
||||
Connection.Free;
|
||||
@@ -117,19 +155,18 @@ end;
|
||||
|
||||
procedure TfrmSyncDB.comboTargetDatabaseChange(Sender: TObject);
|
||||
var
|
||||
Parameters: TConnectionParameters;
|
||||
Connection: TDBConnection;
|
||||
Objects: TDBObjectList;
|
||||
Obj: TDBObject;
|
||||
DBSelected: Boolean;
|
||||
begin
|
||||
// Populate table drop down
|
||||
comboTargetTable.Clear;
|
||||
if comboTargetDatabase.ItemIndex > 0 then begin
|
||||
Parameters := LoadConnectionParams(comboTargetServer.Text);
|
||||
Connection := Parameters.CreateConnection(Self);
|
||||
Connection.OnLog := MainForm.LogSQL;
|
||||
Connection.LogPrefix := comboTargetServer.Text;
|
||||
Connection.Active := True;
|
||||
DBSelected := comboTargetDatabase.ItemIndex > 0;
|
||||
lblTargetTable.Enabled := DBSelected;
|
||||
comboTargetTable.Enabled := DBSelected;
|
||||
if DBSelected then begin
|
||||
Connection := CreateTargetConnection;
|
||||
Objects := Connection.GetDBObjects(comboTargetDatabase.Text);
|
||||
for Obj in Objects do begin
|
||||
if Obj.NodeType = lntTable then
|
||||
@@ -143,6 +180,86 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
procedure TfrmSyncDB.treeDifferencesGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode;
|
||||
Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer);
|
||||
var
|
||||
Diff: PDiffObject;
|
||||
begin
|
||||
if Kind in [ikNormal, ikSelected] then begin
|
||||
case Sender.GetNodeLevel(Node) of
|
||||
0: begin
|
||||
Diff := Sender.GetNodeData(Node);
|
||||
ImageIndex := Diff.DBObject.ImageIndex;
|
||||
Ghosted := Diff.Count = 0;
|
||||
end;
|
||||
1: begin
|
||||
Diff := Sender.GetNodeData(Node.Parent);
|
||||
case Diff^[Node.Index].DiffType of
|
||||
diCreate: ImageIndex := 45;
|
||||
diAlter: ImageIndex := 42;
|
||||
diInsert: ImageIndex := 41;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TfrmSyncDB.treeDifferencesGetNodeDataSize(Sender: TBaseVirtualTree;
|
||||
var NodeDataSize: Integer);
|
||||
begin
|
||||
NodeDataSize := SizeOf(PDiffObject);
|
||||
end;
|
||||
|
||||
|
||||
procedure TfrmSyncDB.treeDifferencesGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
|
||||
Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
|
||||
var
|
||||
Diff: PDiffObject;
|
||||
begin
|
||||
case Sender.GetNodeLevel(Node) of
|
||||
0: begin
|
||||
Diff := Sender.GetNodeData(Node);
|
||||
CellText := Diff.DBObject.Name;
|
||||
end;
|
||||
1: begin
|
||||
Diff := Sender.GetNodeData(Node.Parent);
|
||||
CellText := Diff^[Node.Index].Title;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TfrmSyncDB.treeDifferencesInitChildren(Sender: TBaseVirtualTree; Node: PVirtualNode;
|
||||
var ChildCount: Cardinal);
|
||||
var
|
||||
Diff: PDiffObject;
|
||||
begin
|
||||
case Sender.GetNodeLevel(Node) of
|
||||
0: begin
|
||||
Diff := Sender.GetNodeData(Node);
|
||||
ChildCount := Diff.Count;
|
||||
end;
|
||||
1: ChildCount := 0;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TfrmSyncDB.treeDifferencesInitNode(Sender: TBaseVirtualTree; ParentNode,
|
||||
Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
|
||||
var
|
||||
Diff: PDiffObject;
|
||||
begin
|
||||
if Sender.GetNodeLevel(Node) = 0 then begin
|
||||
Diff := Sender.GetNodeData(Node);
|
||||
if Diff.Count > 0 then
|
||||
Include(InitialStates, ivsHasChildren);
|
||||
end;
|
||||
Node.CheckType := ctTriStateCheckBox;
|
||||
Node.CheckState := csUncheckedNormal;
|
||||
end;
|
||||
|
||||
|
||||
procedure TfrmSyncDB.treeSourceChange(Sender: TBaseVirtualTree; Node: PVirtualNode);
|
||||
begin
|
||||
// Clone logic of MainForm.DBtree
|
||||
@@ -152,15 +269,19 @@ end;
|
||||
|
||||
procedure TfrmSyncDB.treeSourceChecked(Sender: TBaseVirtualTree; Node: PVirtualNode);
|
||||
var
|
||||
DB: PVirtualNode;
|
||||
Sess, DB: PVirtualNode;
|
||||
begin
|
||||
// Uncheck table nodes other than current checked one
|
||||
if (Sender.GetNodeLevel(Node) = 1) and (Node.CheckState in CheckedStates) then begin
|
||||
DB := Sender.GetFirstChild(Node.Parent);
|
||||
while Assigned(DB) do begin
|
||||
if DB <> Node then
|
||||
Sender.CheckState[DB] := csUncheckedNormal;
|
||||
DB := Sender.GetNextSibling(DB);
|
||||
Sess := Sender.GetFirstChild(nil);
|
||||
while Assigned(Sess) do begin
|
||||
DB := Sender.GetFirstChild(Sess);
|
||||
while Assigned(DB) do begin
|
||||
if DB <> Node then
|
||||
Sender.CheckState[DB] := csUncheckedNormal;
|
||||
DB := Sender.GetNextSibling(DB);
|
||||
end;
|
||||
Sess := Sender.GetNextSibling(Sess);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
@@ -233,4 +354,88 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
function TfrmSyncDB.CreateTargetConnection: TDBConnection;
|
||||
var
|
||||
Parameters: TConnectionParameters;
|
||||
begin
|
||||
// Create target connection
|
||||
Parameters := LoadConnectionParams(comboTargetServer.Text);
|
||||
Result := Parameters.CreateConnection(Self);
|
||||
Result.OnLog := MainForm.LogSQL;
|
||||
Result.LogPrefix := comboTargetServer.Text;
|
||||
Result.Active := True;
|
||||
end;
|
||||
|
||||
|
||||
procedure TfrmSyncDB.btnAnalyzeClick(Sender: TObject);
|
||||
var
|
||||
Sess, DB, Table: PVirtualNode;
|
||||
Connection: TDBConnection;
|
||||
Objects, TargetObjects: TDBObjectList;
|
||||
SourceObj, TargetObj: TDBObject;
|
||||
DBObj: PDBObject;
|
||||
ObjExists: Boolean;
|
||||
TargetDB: String;
|
||||
Diff: TDiffObject;
|
||||
begin
|
||||
// Analyze differences, display these in diff tree
|
||||
Objects := TDBObjectList.Create(False);
|
||||
Sess := treeSource.GetFirstChild(nil);
|
||||
while Assigned(Sess) do begin
|
||||
DB := treeSource.GetFirstChild(Sess);
|
||||
while Assigned(DB) do begin
|
||||
if treeSource.ChildrenInitialized[DB] then begin
|
||||
Table := treeSource.GetFirstChild(DB);
|
||||
while Assigned(Table) do begin
|
||||
if treeSource.CheckState[Table] in CheckedStates then begin
|
||||
DBObj := treeSource.GetNodeData(Table);
|
||||
Objects.Add(DBObj^);
|
||||
end;
|
||||
Table := treeSource.GetNextSibling(Table);
|
||||
end;
|
||||
end;
|
||||
DB := treeSource.GetNextSibling(DB);
|
||||
end;
|
||||
Sess := treeSource.GetNextSibling(Sess);
|
||||
end;
|
||||
|
||||
Connection := CreateTargetConnection;
|
||||
if comboTargetDatabase.ItemIndex = 0 then
|
||||
TargetDB := Objects[0].Database
|
||||
else
|
||||
TargetDB := comboTargetDatabase.Text;
|
||||
|
||||
// Check for existance in target db
|
||||
TargetObjects := Connection.GetDBObjects(TargetDB);
|
||||
for SourceObj in Objects do begin
|
||||
Diff := TDiffObject.Create(True);
|
||||
Diff.DBObject := SourceObj;
|
||||
treeDifferences.BeginUpdate;
|
||||
treeDifferences.AddChild(nil, PDiffObject(Diff));
|
||||
ObjExists := False;
|
||||
for TargetObj in TargetObjects do begin
|
||||
if (TargetObj.Name = SourceObj.Name) and (TargetObj.NodeType = SourceObj.NodeType) then begin
|
||||
ObjExists := True;
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
if not ObjExists then begin
|
||||
Diff.AddItem('Create missing '+LowerCase(SourceObj.ObjType), SourceObj.CreateCode, diCreate);
|
||||
end;
|
||||
treeDifferences.EndUpdate;
|
||||
end;
|
||||
|
||||
|
||||
// Throw target connection away
|
||||
Connection.Active := False;
|
||||
Connection.Free;
|
||||
end;
|
||||
|
||||
|
||||
procedure TfrmSyncDB.btnApplyClick(Sender: TObject);
|
||||
begin
|
||||
// Apply selected differences on target database
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
||||
Reference in New Issue
Block a user