Issue #1289: Add some basic code to the "analyze" button, which detects missing tables in target database.

This commit is contained in:
Ansgar Becker
2011-08-26 06:57:31 +00:00
parent 12b158ba1c
commit 4ccb6391df
2 changed files with 257 additions and 41 deletions

View File

@@ -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

View File

@@ -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.