diff --git a/source/syncdb.dfm b/source/syncdb.dfm index 40984833..7f5b8db7 100644 --- a/source/syncdb.dfm +++ b/source/syncdb.dfm @@ -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 diff --git a/source/syncdb.pas b/source/syncdb.pas index 46ab029a..30bdebe8 100644 --- a/source/syncdb.pas +++ b/source/syncdb.pas @@ -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) + 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.