From 91be516f0f2382cdd1af5c4ef11f485916b8c43c Mon Sep 17 00:00:00 2001 From: Ansgar Becker Date: Sun, 9 Mar 2025 20:09:37 +0100 Subject: [PATCH] Issue #1482: add copy table dialog --- heidisql.lpi | 7 + heidisql.lpr | 2 +- readme-unit-conversion.txt | 3 +- source/apphelpers.pas | 1 + source/copytable.lfm | 197 ++++++++++++++ source/copytable.pas | 507 +++++++++++++++++++++++++++++++++++++ source/main.pas | 13 +- 7 files changed, 722 insertions(+), 8 deletions(-) create mode 100644 source/copytable.lfm create mode 100644 source/copytable.pas diff --git a/heidisql.lpi b/heidisql.lpi index 4db9e846..96c4cb46 100644 --- a/heidisql.lpi +++ b/heidisql.lpi @@ -173,6 +173,13 @@ + + + + + + + diff --git a/heidisql.lpr b/heidisql.lpr index d6a5aae8..699e84e0 100644 --- a/heidisql.lpr +++ b/heidisql.lpr @@ -17,7 +17,7 @@ uses dbstructures, dbstructures.mysql, About, generic_types, dbstructures.interbase, dbstructures.mssql, dbstructures.postgresql, dbstructures.sqlite, change_password, loginform, data_sorting, extra_controls, - column_selection, loaddata, csv_detector, createdatabase, editvar {, printlist (EnablePrint not defined) } + column_selection, loaddata, csv_detector, createdatabase, editvar, copytable {, printlist (EnablePrint not defined) } ; {$R *.res} diff --git a/readme-unit-conversion.txt b/readme-unit-conversion.txt index 759eabca..935084ef 100644 --- a/readme-unit-conversion.txt +++ b/readme-unit-conversion.txt @@ -17,9 +17,10 @@ UF = both * UF replace TVirtualStringTree|Columns => TLazVirtualStringTree|Header.Columns * UF replace TButtonedEdit => TEditButton (+ use EditBtn unit) * UF if TButton has Images + ImageIndex: replace TButton => TSpeedButton (note: ModalResult not supported!) +* UF if TButton has Style + DropDownMenu: replace TButton => TSpeedButton with ImageIndex 108 and let OnClick activate the TPopupMenu * U open unit, accept removal of unknown properties * U press Shift+F11 to add unit+form to project Later: -* TUpDown has minimum Min value of 32768 and maximum Max of 32767. Remove and activate TEdit.NumbersOnly \ No newline at end of file +* TUpDown has minimum Min value of 32768 and maximum Max of 32767. Remove and activate TEdit.NumbersOnly diff --git a/source/apphelpers.pas b/source/apphelpers.pas index c03c3ab9..fc7d7e57 100644 --- a/source/apphelpers.pas +++ b/source/apphelpers.pas @@ -4282,6 +4282,7 @@ end; function TAppSettings.ReadIntDpiAware(Index: TAppSettingIndex; AControl: TControl; FormatName: String=''; Default: Integer=0): Integer; begin + // Todo: take a forms DesignTimePPI into account Result := ReadInt(Index, FormatName, Default); //Result := Round(Result * AControl.ScaleFactor); end; diff --git a/source/copytable.lfm b/source/copytable.lfm new file mode 100644 index 00000000..386c5ebb --- /dev/null +++ b/source/copytable.lfm @@ -0,0 +1,197 @@ +object CopyTableForm: TCopyTableForm + Left = 393 + Height = 380 + Top = 115 + Width = 455 + Caption = 'Copy Table...' + ClientHeight = 380 + ClientWidth = 455 + Color = clBtnFace + DesignTimePPI = 120 + Font.Color = clWindowText + Font.Height = -15 + Font.Name = 'Tahoma' + OnClose = FormClose + OnCreate = FormCreate + OnResize = FormResize + OnShow = FormShow + Position = poMainFormCenter + object lblNewTablename: TLabel + Left = 10 + Height = 18 + Top = 10 + Width = 183 + Caption = 'Copy "%s" to new db.table:' + end + object lblItems: TLabel + Left = 10 + Height = 18 + Top = 64 + Width = 210 + Caption = 'Elements to create in new table:' + end + object lblWhere: TLabel + Left = 10 + Height = 18 + Top = 207 + Width = 210 + Anchors = [akLeft, akBottom] + Caption = 'WHERE clause for data copying:' + end + object editNewTablename: TEdit + Left = 199 + Height = 26 + Top = 30 + Width = 246 + TabOrder = 1 + OnChange = editNewTablenameChange + end + object btnCancel: TButton + Left = 341 + Height = 31 + Top = 339 + Width = 104 + Anchors = [akRight, akBottom] + Cancel = True + Caption = 'Cancel' + ModalResult = 2 + TabOrder = 5 + end + object comboDatabase: TComboBox + Left = 10 + Height = 26 + Top = 30 + Width = 181 + ItemHeight = 18 + Style = csDropDownList + TabOrder = 0 + end + object btnOK: TButton + Left = 230 + Height = 31 + Top = 339 + Width = 104 + Anchors = [akRight, akBottom] + Caption = 'OK' + Default = True + ModalResult = 1 + TabOrder = 4 + OnClick = btnOKClick + end + object TreeElements: TLazVirtualStringTree + Left = 10 + Height = 110 + Top = 88 + Width = 435 + Anchors = [akTop, akLeft, akRight, akBottom] + Header.AutoSizeIndex = 0 + Header.Columns = <> + Header.MainColumn = -1 + Images = MainForm.ImageListIcons8 + TabOrder = 2 + TreeOptions.AutoOptions = [toAutoDropExpand, toAutoScroll, toAutoScrollOnExpand, toAutoTristateTracking, toAutoDeleteMovedNodes, toAutoChangeScale] + TreeOptions.MiscOptions = [toAcceptOLEDrop, toCheckSupport, toFullRepaintOnResize, toInitOnSave, toToggleOnDblClick, toWheelPanning, toEditOnClick] + TreeOptions.PaintOptions = [toHotTrack, toShowButtons, toShowDropmark, toShowRoot, toShowTreeLines, toThemeAware, toUseBlendedImages, toUseExplorerTheme, toHideTreeLinesIfThemed] + OnChecked = TreeElementsChecked + OnGetText = TreeElementsGetText + OnGetImageIndex = TreeElementsGetImageIndex + OnInitChildren = TreeElementsInitChildren + OnInitNode = TreeElementsInitNode + end + inline MemoFilter: TSynEdit + Left = 10 + Height = 91 + Top = 240 + Width = 435 + Anchors = [akLeft, akRight, akBottom] + Font.Color = clGrayText + Font.Height = -16 + Font.Name = 'Courier New' + Font.Pitch = fpFixed + Font.Quality = fqNonAntialiased + ParentColor = False + ParentFont = False + TabOrder = 3 + Gutter.Width = 72 + Gutter.MouseActions = <> + RightGutter.Width = 0 + RightGutter.MouseActions = <> + Highlighter = MainForm.SynSQLSynUsed + Keystrokes = <> + MouseActions = <> + MouseTextActions = <> + MouseSelActions = <> + Options = [eoAutoIndent, eoGroupUndo, eoKeepCaretX, eoShowScrollHint, eoSmartTabs, eoTabIndent, eoDragDropEditing] + MouseOptions = [emDragDropEditing] + VisibleSpecialChars = [vscSpace, vscTabAtLast] + SelectedColor.BackPriority = 50 + SelectedColor.ForePriority = 50 + SelectedColor.FramePriority = 50 + SelectedColor.BoldPriority = 50 + SelectedColor.ItalicPriority = 50 + SelectedColor.UnderlinePriority = 50 + SelectedColor.StrikeOutPriority = 50 + BracketHighlightStyle = sbhsBoth + BracketMatchColor.Background = clNone + BracketMatchColor.Foreground = clNone + BracketMatchColor.Style = [fsBold] + FoldedCodeColor.Background = clNone + FoldedCodeColor.Foreground = clGray + FoldedCodeColor.FrameColor = clGray + MouseLinkColor.Background = clNone + MouseLinkColor.Foreground = clBlue + LineHighlightColor.Background = clNone + LineHighlightColor.Foreground = clNone + inline SynLeftGutterPartList1: TSynGutterPartList + object SynGutterMarks1: TSynGutterMarks + Width = 30 + MouseActions = <> + end + object SynGutterLineNumber1: TSynGutterLineNumber + Width = 21 + MouseActions = <> + MarkupInfo.Background = clBtnFace + MarkupInfo.Foreground = clNone + DigitCount = 2 + ShowOnlyLineNumbersMultiplesOf = 1 + ZeroStart = False + LeadingZeros = False + end + object SynGutterChanges1: TSynGutterChanges + Width = 5 + MouseActions = <> + ModifiedColor = 59900 + SavedColor = clGreen + end + object SynGutterSeparator1: TSynGutterSeparator + Width = 3 + MouseActions = <> + MarkupInfo.Background = clWhite + MarkupInfo.Foreground = clGray + end + object SynGutterCodeFolding1: TSynGutterCodeFolding + Width = 13 + MouseActions = <> + MarkupInfo.Background = clNone + MarkupInfo.Foreground = clGray + MouseActionsExpanded = <> + MouseActionsCollapsed = <> + end + end + end + object btnRecentFilters: TSpeedButton + Left = 310 + Height = 28 + Top = 204 + Width = 135 + Anchors = [akRight, akBottom] + Caption = 'Recent filters' + Images = MainForm.ImageListIcons8 + ImageIndex = 108 + OnClick = btnRecentFiltersClick + end + object popupRecentFilters: TPopupMenu + Left = 260 + Top = 200 + end +end diff --git a/source/copytable.pas b/source/copytable.pas new file mode 100644 index 00000000..90885de9 --- /dev/null +++ b/source/copytable.pas @@ -0,0 +1,507 @@ +unit copytable; + +{$mode delphi}{$H+} + +interface + +uses + SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, extra_controls, + dbconnection, dbstructures, dbstructures.mysql, laz.VirtualTrees, SynEdit, Menus, + Buttons; + +type + TCopyTableForm = class(TExtForm) + editNewTablename: TEdit; + lblNewTablename: TLabel; + btnCancel: TButton; + comboDatabase: TComboBox; + btnOK: TButton; + TreeElements: TLazVirtualStringTree; + MemoFilter: TSynEdit; + lblItems: TLabel; + lblWhere: TLabel; + btnRecentFilters: TSpeedButton; + popupRecentFilters: TPopupMenu; + procedure editNewTablenameChange(Sender: TObject); + procedure FormShow(Sender: TObject); + procedure btnOKClick(Sender: TObject); + procedure FormCreate(Sender: TObject); + procedure TreeElementsGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; + Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); + procedure TreeElementsInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode; + var InitialStates: TVirtualNodeInitStates); + procedure TreeElementsGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; + Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer); + procedure TreeElementsInitChildren(Sender: TBaseVirtualTree; Node: PVirtualNode; + var ChildCount: Cardinal); + procedure TreeElementsChecked(Sender: TBaseVirtualTree; Node: PVirtualNode); + procedure FormClose(Sender: TObject; var Action: TCloseAction); + procedure btnRecentFiltersClick(Sender: TObject); + procedure RecentFilterClick(Sender: TObject); + procedure FormResize(Sender: TObject); + private + { Private declarations } + FDBObj: TDBObject; + FConnection: TDBConnection; + FColumns: TTableColumnList; + FKeys: TTableKeyList; + FForeignKeys: TForeignKeyList; + public + { Public declarations } + end; + + +implementation + +uses apphelpers, main; + +const + nColumns = 0; + nKeys = 1; + nForeignKeys = 2; + nData = 3; + +{$R *.lfm} +{$I const.inc} + + + +procedure TCopyTableForm.FormCreate(Sender: TObject); +begin + HasSizeGrip := True; + MainForm.SetupSynEditors(Self); + FixVT(TreeElements); +end; + + +procedure TCopyTableForm.FormResize(Sender: TObject); +var + HalfWidth: Integer; +const + Space: Integer=8; +begin + // Give both dropdown and edit box the same width. See http://www.heidisql.com/forum.php?t=11039 + HalfWidth := (ClientWidth - (3*Space)) div 2; + comboDatabase.Width := HalfWidth; + editNewTablename.Width := HalfWidth; + editNewTablename.Left := comboDatabase.Left + comboDatabase.Width + Space; +end; + + +procedure TCopyTableForm.FormShow(Sender: TObject); +var + Filter: String; + Obj: PDBObject; + i: Integer; + Item: TMenuItem; + Tree: TVirtualStringTree; +begin + // Todo: check if resizing a form here drags its controls with it + //Width := AppSettings.ReadIntDpiAware(asCopyTableWindowWidth, Self); + //Height := AppSettings.ReadIntDpiAware(asCopyTableWindowHeight, Self); + if Mainform.DBtree.Focused then + Tree := Mainform.DBtree + else + Tree := Mainform.ListTables; + Obj := Tree.GetNodeData(Tree.FocusedNode); + FDBObj := Obj^; + FConnection := FDBObj.Connection; + editNewTablename.Text := FDBObj.Name + '_copy'; + editNewTablename.SetFocus; + lblNewTablename.Caption := f_('Copy "%s" to new db.table:', [FDBObj.Name]); + editNewTablename.SetFocus; + + // Select TargetDatabase + comboDatabase.Items.Clear; + comboDatabase.Items.Assign(FConnection.AllDatabases); + comboDatabase.ItemIndex := comboDatabase.Items.IndexOf(Mainform.ActiveDatabase); + if comboDatabase.ItemIndex = -1 then + comboDatabase.ItemIndex := 0; + + // Fetch columns and key structures from table or view + case FDBObj.NodeType of + lntTable, lntView: begin + FColumns := FDBObj.TableColumns; + FKeys := FDBObj.TableKeys; + FForeignKeys := FDBObj.TableForeignKeys; + end; + else raise Exception.CreateFmt(_('Neither table nor view: %s'), [FDBObj.Name]); + end; + + // Reset options tree + TreeElements.Clear; + TreeElements.RootNodeCount := 4; + + // Load recent WHERE clauses from registry into dropdown menu + popupRecentFilters.Items.Clear; + for i:=1 to 20 do begin + Filter := AppSettings.ReadString(asCopyTableRecentFilter, IntToStr(i)); + if Filter.IsEmpty then + Continue; + Item := TMenuItem.Create(popupRecentFilters); + Item.Caption := IntToStr(i) + ' ' + StrEllipsis(Filter, 100); + Item.Hint := Filter; + Item.OnClick := RecentFilterClick; + popupRecentFilters.Items.Add(Item); + end; + +end; + + + +procedure TCopyTableForm.FormClose(Sender: TObject; var Action: TCloseAction); +var + Node: PVirtualNode; + Option: TAppSettingIndex; + Filter: String; + NewValues: TStringList; + i: Integer; +begin + // Save first level node check options + Node := TreeElements.GetFirst; + while Assigned(Node) do begin + case Node.Index of + nColumns: Option := asCopyTableColumns; + nKeys: Option := asCopyTableKeys; + nForeignKeys: Option := asCopyTableForeignKeys; + nData: Option := asCopyTableData; + else raise Exception.Create(_(SUnhandledNodeIndex)); + end; + if not (vsDisabled in Node.States) then + AppSettings.WriteBool(Option, Node.CheckState in CheckedStates); + Node := TreeElements.GetNextSibling(Node); + end; + // Store recent filters + if MemoFilter.Enabled and (MemoFilter.GetTextLen > 0) then begin + NewValues := TStringList.Create; + NewValues.Add(MemoFilter.Text); + for i:=1 to 20 do begin + Filter := AppSettings.ReadString(asCopyTableRecentFilter, IntToStr(i)); + if Filter.IsEmpty then + Continue; + if NewValues.IndexOf(Filter) = -1 then + NewValues.Add(Filter); + AppSettings.DeleteValue(asCopyTableRecentFilter, IntToStr(i)); + end; + for i:=0 to NewValues.Count-1 do begin + AppSettings.WriteString(asCopyTableRecentFilter, NewValues[i], IntToStr(i)); + end; + end; + // Store GUI setup + AppSettings.WriteIntDpiAware(asCopyTableWindowWidth, Self, Width); + AppSettings.WriteIntDpiAware(asCopyTableWindowHeight, Self, Height); +end; + + +procedure TCopyTableForm.TreeElementsChecked(Sender: TBaseVirtualTree; Node: PVirtualNode); +begin + // Disable WHERE memo if "Data" was unselected + if (Sender.GetNodeLevel(Node) = 0) and (Node.Index = nData) then begin + MemoFilter.Enabled := Node.CheckState = csCheckedNormal; + btnRecentFilters.Enabled := MemoFilter.Enabled; + if MemoFilter.Enabled then begin + MemoFilter.Highlighter := MainForm.SynSQLSynUsed; + MemoFilter.Color := GetThemeColor(clWindow); + end else begin + MemoFilter.Highlighter := nil; + MemoFilter.Color := GetThemeColor(clBtnFace); + end; + end; +end; + + +procedure TCopyTableForm.TreeElementsGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; + Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: TImageIndex); +begin + // Get node index + if not (Kind in [ikNormal, ikSelected]) then Exit; + case Sender.GetNodeLevel(Node) of + 0: case Node.Index of + nColumns: ImageIndex := ICONINDEX_FIELD; + nKeys: ImageIndex := 13; + nForeignKeys: ImageIndex := ICONINDEX_FOREIGNKEY; + nData: ImageIndex := 41; + else raise Exception.Create(_(SUnhandledNodeIndex)); + end; + 1: case Node.Parent.Index of + nColumns: ImageIndex := ICONINDEX_FIELD; + nKeys: ImageIndex := FKeys[Node.Index].ImageIndex; + nForeignKeys: ImageIndex := ICONINDEX_FOREIGNKEY; + else raise Exception.Create(_(SUnhandledNodeIndex)); + end; + end; +end; + + +procedure TCopyTableForm.TreeElementsGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; + Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); +var + CheckedCount: Integer; + Child: PVirtualNode; +begin + // Get node text + case Sender.GetNodeLevel(Node) of + 0: begin + case Node.Index of + nColumns: CellText := _('Columns'); + nKeys: CellText := _('Indexes'); + nForeignKeys: CellText := _('Foreign keys'); + nData: CellText := f_('Data (%s rows)', [FormatNumber(FDBObj.RowCount(False))]); + else raise Exception.Create(_(SUnhandledNodeIndex)); + end; + if Node.Index <> nData then begin + CheckedCount := 0; + Child := Sender.GetFirstChild(Node); + while Assigned(Child) do begin + if Child.CheckState in CheckedStates then + Inc(CheckedCount); + Child := Sender.GetNextSibling(Child); + end; + CellText := CellText + ' ('+FormatNumber(CheckedCount)+'/'+FormatNumber(Sender.ChildCount[Node])+')'; + end; + end; + 1: case Node.Parent.Index of + nColumns: CellText := FColumns[Node.Index].Name; + nKeys: CellText := FKeys[Node.Index].Name; + nForeignKeys: CellText := FForeignKeys[Node.Index].KeyName; + else raise Exception.Create(_(SUnhandledNodeIndex)); + end; + end; +end; + + +procedure TCopyTableForm.TreeElementsInitChildren(Sender: TBaseVirtualTree; Node: PVirtualNode; + var ChildCount: Cardinal); +begin + // Set child node count + case Sender.GetNodeLevel(Node) of + 0: case Node.Index of + nColumns: ChildCount := FColumns.Count; + nKeys: ChildCount := FKeys.Count; + nForeignKeys: ChildCount := FForeignKeys.Count; + nData: ChildCount := 0; + else raise Exception.Create(_(SUnhandledNodeIndex)); + end; + else ChildCount := 0; + end; +end; + + +procedure TCopyTableForm.TreeElementsInitNode(Sender: TBaseVirtualTree; ParentNode, + Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates); +var + Option: TAppSettingIndex; + ChildCount: Integer; +begin + // First three upper nodes mostly have child nodes + Node.CheckType := ctTriStateCheckBox; + case Sender.GetNodeLevel(Node) of + 0: begin + case Node.Index of + nColumns: begin Option := asCopyTableColumns; ChildCount := FColumns.Count; end; + nKeys: begin Option := asCopyTableKeys; ChildCount := FKeys.Count; end; + nForeignKeys: begin Option := asCopyTableForeignKeys; ChildCount := FForeignKeys.Count; end; + nData: begin Option := asCopyTableData; ChildCount := -1; end; + else raise Exception.Create(_(SUnhandledNodeIndex)); + end; + if ChildCount > 0 then + Include(InitialStates, ivsHasChildren); + if (ChildCount = 0) or ((Node.Index = nData) and (FDBObj.RowCount(False) = 0)) then + Node.States := Node.States + [vsDisabled] + else if AppSettings.ReadBool(Option) then + Node.CheckState := csCheckedNormal; + (Sender as TVirtualStringTree).OnChecked(Sender, Node); + end; + + 1: if Node.Parent.CheckState in CheckedStates then + Node.CheckState := csCheckedNormal; + end; +end; + + +procedure TCopyTableForm.btnRecentFiltersClick(Sender: TObject); +var + btn: TControl; +begin + // A split button does not drop its menu down when the normal button area is clicked. Do that by hand. + btn := Sender as TControl; + popupRecentFilters.Popup(btn.ClientOrigin.X, btn.ClientOrigin.Y+btn.Height); +end; + + +procedure TCopyTableForm.RecentFilterClick(Sender: TObject); +var + Item: TMenuItem; +begin + // Load recent filter + Item := Sender as TMenuItem; + MemoFilter.SelectAll; + MemoFilter.SelText := Item.Hint; +end; + + +procedure TCopyTableForm.editNewTablenameChange(Sender: TObject); +begin + // Disable OK button as long as table name is empty + btnOK.Enabled := editNewTablename.Text <> ''; +end; + + +procedure TCopyTableForm.btnOKClick(Sender: TObject); +var + CreateCode, InsertCode, TargetTable, DataCols, Msg: String; + TableExistence, AutoIncName: String; + ParentNode, Node: PVirtualNode; + DoData, AutoIncGetsKey, AutoIncRemoved, TableHasAutoInc: Boolean; + SelectedColumns: TTableColumnList; + SelectedKeys: TTableKeyList; + SelectedForeignKeys: TForeignKeyList; + Column: TTableColumn; + Key: TTableKey; + ForeignKey: TForeignKey; + ClausePattern: String; +begin + // Compose and run CREATE query + + TargetTable := FConnection.QuotedDbAndTableName(comboDatabase.Text, editNewTablename.Text); + ClausePattern := CodeIndent + '%s,' + sLineBreak; + + // Watch out if target table exists + try + TableExistence := FConnection.GetVar('SELECT 1 FROM '+ + FConnection.QuoteIdent(comboDatabase.Text)+'.'+FConnection.QuoteIdent(editNewTablename.Text)); + except + TableExistence := ''; + end; + if TableExistence <> '' then begin + if MessageDialog(_('Target table exists. Drop it and overwrite?'), mtConfirmation, [mbYes, mbCancel]) = mrCancel then begin + ModalResult := mrNone; + Exit; + end; + FConnection.Query('DROP TABLE '+TargetTable); + FConnection.ShowWarnings; + end; + + Screen.Cursor := crHourglass; + MainForm.ShowStatusMsg(_('Generating SQL code ...')); + DataCols := ''; + SelectedColumns := TTableColumnList.Create(False); + SelectedKeys := TTableKeyList.Create(False); + SelectedForeignKeys := TForeignKeyList.Create(False); + DoData := False; + ParentNode := TreeElements.GetFirst; + while Assigned(ParentNode) do begin + Node := TreeElements.GetFirstChild(ParentNode); + while Assigned(Node) do begin + if Node.CheckState in CheckedStates then begin + case ParentNode.Index of + nColumns: SelectedColumns.Add(FColumns[Node.Index]); + nKeys: SelectedKeys.Add(FKeys[Node.Index]); + nForeignkeys: SelectedForeignKeys.Add(FForeignKeys[Node.Index]); + else raise Exception.Create(_(SUnhandledNodeIndex)); + end; + end; + Node := TreeElements.GetNextSibling(Node); + end; + if (ParentNode.Index = nData) then + DoData := ParentNode.CheckState in CheckedStates; + ParentNode := TreeElements.GetNextSibling(ParentNode); + end; + + CreateCode := ''; + TableHasAutoInc := False; + + // Columns code. Remove auto_increment attribute if pkey was unchecked, to overcome + // "there can be only one auto column and it must be defined as a key" + AutoIncName := 'unknown'; + for Column in SelectedColumns do begin + AutoIncGetsKey := False; + AutoIncRemoved := False; + AutoIncName := Column.AutoIncName; + if Column.DefaultType = cdtAutoInc then begin + for Key in SelectedKeys do begin + // Don't check index type, MySQL allows auto-increment columns on nearly all indexes + if Key.Columns.IndexOf(Column.Name) > -1 then begin + AutoIncGetsKey := True; + Break; + end; + end; + if not AutoIncGetsKey then begin + Column.DefaultType := cdtNothing; + AutoIncRemoved := True; + end; + end; + TableHasAutoInc := TableHasAutoInc or (Column.DefaultType = cdtAutoInc); + CreateCode := CreateCode + Format(ClausePattern, [Column.SQLCode]); + if AutoIncRemoved then + Column.DefaultType := cdtAutoInc; + // Use this column for the INSERT..SELECT query only if it's not a virtual one + if Column.Virtuality.IsEmpty then + DataCols := DataCols + FConnection.QuoteIdent(Column.Name) + ', '; + end; + + // Indexes code + for Key in SelectedKeys do + CreateCode := CreateCode + Format(ClausePattern, [Key.SQLCode]); + + // Foreign keys code + for ForeignKey in SelectedForeignKeys do + CreateCode := CreateCode + Format(ClausePattern, [ForeignKey.SQLCode(False)]); + + // Finish code + Delete(CreateCode, Length(CreateCode)-2, 3); + CreateCode := 'CREATE TABLE '+TargetTable+' ('+CRLF+CreateCode+CRLF+')'+CRLF; + + // Add collation and engine clauses + if FDBObj.Collation <> '' then + CreateCode := CreateCode + ' COLLATE ' + FConnection.EscapeString(FDBObj.Collation); + if FDBObj.Engine <> '' then begin + if FConnection.ServerVersionInt < 40018 then + CreateCode := CreateCode + ' TYPE=' + FDBObj.Engine + else + CreateCode := CreateCode + ' ENGINE=' + FDBObj.Engine; + end; + if FDBObj.RowFormat <> '' then + CreateCode := CreateCode + ' ROW_FORMAT=' + FDBObj.RowFormat; + if (FDBObj.AutoInc > -1) and TableHasAutoInc then + CreateCode := CreateCode + ' AUTO_INCREMENT=' + IntToStr(FDBObj.AutoInc); + if FDBObj.Comment <> '' then + CreateCode := CreateCode + ' COMMENT=' + FConnection.EscapeString(FDBObj.Comment); + + // Add INSERT .. SELECT .. FROM OrgTable clause + InsertCode := ''; + if DoData and (DataCols <> '') then begin + DataCols := Trim(DataCols); + Delete(DataCols, Length(DataCols), 1); + InsertCode := 'INSERT INTO '+TargetTable+' ('+DataCols+') SELECT ' + DataCols + ' FROM ' + FDBObj.QuotedName; + if MemoFilter.GetTextLen > 0 then + InsertCode := InsertCode + ' WHERE ' + MemoFilter.Text; + end; + + // Run query and refresh list + try + MainForm.ShowStatusMsg(_('Creating table ...')); + FConnection.Query(CreateCode); + FConnection.ShowWarnings; + if InsertCode <> '' then begin + FConnection.Query(InsertCode); + FConnection.ShowWarnings; + end; + // actRefresh takes care of whether the table editor is open + // See also issue #1597 + MainForm.actRefresh.Execute + except + on E:EDbError do begin + Screen.Cursor := crDefault; + Msg := E.Message; + if FConnection.LastErrorCode = ER_WRONG_AUTO_KEY then + Msg := Msg + CRLF + CRLF + f_('Please select the required index for the %s flag.', [AutoIncName]); + ErrorDialog(Msg); + ModalResult := mrNone; + end; + end; + MainForm.ShowStatusMsg; + Screen.Cursor := crDefault; +end; + +end. diff --git a/source/main.pas b/source/main.pas index f783afdc..b02aab1d 100644 --- a/source/main.pas +++ b/source/main.pas @@ -1411,7 +1411,8 @@ const implementation uses - FileInfo, winpeimagereader, elfreader, machoreader, About, data_sorting, column_selection, loaddata, editvar; + FileInfo, winpeimagereader, elfreader, machoreader, About, data_sorting, column_selection, loaddata, editvar, + copytable; {$R *.lfm} @@ -3058,13 +3059,13 @@ end; procedure TMainForm.actCopyTableExecute(Sender: TObject); -//var -// Dialog: TCopyTableForm; +var + Dialog: TCopyTableForm; begin // copy table - //Dialog := TCopyTableForm.Create(Self); - //Dialog.ShowModal; - //Dialog.Free; + Dialog := TCopyTableForm.Create(Self); + Dialog.ShowModal; + Dialog.Free; end;