From c0da6e5a21d58e204efc037a32750cb612910417 Mon Sep 17 00:00:00 2001 From: Ansgar Becker Date: Mon, 14 Jun 2010 00:21:33 +0000 Subject: [PATCH] Refactor "Copy table" dialog: * Replace checkboxes, radio buttons and checklistbox by a VirtualTree using checkbox support * Support selecting/deselecting single indexes * Support foreign keys * Place a SynMemo at the bottom in which the user can type an optional WHERE clause to filter incoming data. Fixes issue #2000. * Move code for SQL generation into TTableColumn etc. --- source/const.inc | 13 +- source/copytable.dfm | 175 ++++++------ source/copytable.pas | 528 ++++++++++++++++++------------------ source/grideditlinks.pas | 24 -- source/helpers.pas | 33 ++- source/main.pas | 23 +- source/mysql_connection.pas | 83 +++++- source/table_editor.pas | 86 +----- 8 files changed, 474 insertions(+), 491 deletions(-) diff --git a/source/const.inc b/source/const.inc index 8b4371ab..afba89f8 100644 --- a/source/const.inc +++ b/source/const.inc @@ -184,12 +184,12 @@ const REGNAME_SELECTDBO_WINHEIGHT = 'SelectDBO_WindowHeight'; REGNAME_SESSMNGR_WINWIDTH = 'SessionManager_WindowWidth'; REGNAME_SESSMNGR_WINHEIGHT = 'SessionManager_WindowHeight'; - REGNAME_COPYTABLE_STRUCDATA = 'CopyTable_Option_StructureData'; - REGVAL_COPYTABLE_STRUCTURE = 0; - REGVAL_COPYTABLE_STRUCTURE_AND_DATA = 1; - DEFAULT_COPYTABLE_STRUCDATA = REGVAL_COPYTABLE_STRUCTURE_AND_DATA; - REGNAME_COPYTABLE_INDEXES = 'CopyTable_Option_WithIndexes'; - REGNAME_COPYTABLE_ALLFIELDS = 'CopyTable_Option_WithAllFields'; + REGNAME_COPYTABLE_WINHEIGHT = 'CopyTable_WindowHeight'; + REGNAME_COPYTABLE_WINWIDTH = 'CopyTable_WindowWidth'; + REGNAME_COPYTABLE_COLUMNS = 'CopyTable_Columns'; + REGNAME_COPYTABLE_KEYS = 'CopyTable_Keys'; + REGNAME_COPYTABLE_FOREIGN = 'CopyTable_ForeignKeys'; + REGNAME_COPYTABLE_DATA = 'CopyTable_Data'; REGNAME_FILTERS = 'RecentFilters'; REGNAME_SERVERVERSION = 'ServerVersion'; REGNAME_LASTCONNECT = 'LastConnect'; @@ -321,6 +321,7 @@ const SContainsNulCharFile = 'This file contains NUL characters. They have been converted to ASCII spaces (SP).'; SContainsNulCharGrid = 'This cell contains NUL characters. They have been converted to ASCII spaces (SP). Press ESC to cancel editing.'; SUnhandledTreeLevel = 'Unhandled tree node level.'; + SUnhandledNodeIndex = 'Unhandled tree node index'; MSG_NOGRIDEDITING = 'Selected columns don''t contain a sufficient set of key columns to allow editing. Please select primary or unique key columns, or just all columns.'; SIdle = 'Idle.'; SUnsupported = 'Not supported by this server'; diff --git a/source/copytable.dfm b/source/copytable.dfm index 5351bd8c..2ba3924e 100644 --- a/source/copytable.dfm +++ b/source/copytable.dfm @@ -1,11 +1,12 @@ object CopyTableForm: TCopyTableForm Left = 393 Top = 115 - BorderStyle = bsDialog Caption = 'Copy Table...' - ClientHeight = 329 - ClientWidth = 302 + ClientHeight = 304 + ClientWidth = 364 Color = clBtnFace + Constraints.MinHeight = 340 + Constraints.MinWidth = 380 Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 @@ -13,127 +14,127 @@ object CopyTableForm: TCopyTableForm Font.Style = [] OldCreateOrder = False Position = poMainFormCenter + OnClose = FormClose OnCreate = FormCreate + OnDestroy = FormDestroy OnShow = FormShow DesignSize = ( - 302 - 329) + 364 + 304) PixelsPerInch = 96 TextHeight = 13 object lblNewTablename: TLabel Left = 8 Top = 8 - Width = 103 + Width = 119 Height = 13 - Caption = 'Copy .. to new table:' + Caption = 'Copy .. to new db.table:' end - object lblTargetDB: TLabel + object lblItems: TLabel Left = 8 - Top = 56 - Width = 84 + Top = 51 + Width = 155 Height = 13 - Caption = 'Target database:' + Caption = 'Elements to create in new table:' + end + object lblWhere: TLabel + Left = 8 + Top = 164 + Width = 155 + Height = 13 + Anchors = [akLeft, akBottom] + Caption = 'WHERE clause for data copying:' end object editNewTablename: TEdit - Left = 8 + Left = 159 Top = 24 - Width = 286 + Width = 197 Height = 21 Anchors = [akLeft, akTop, akRight] - TabOrder = 0 + TabOrder = 1 OnChange = editNewTablenameChange end - object radioStructure: TRadioButton - Left = 154 - Top = 109 - Width = 140 - Height = 17 - Caption = 'Structure' - TabOrder = 3 - OnClick = radioStructureClick - end - object radioStructureAndData: TRadioButton - Left = 154 - Top = 133 - Width = 140 - Height = 17 - Caption = 'Structure and Data' - Checked = True - TabOrder = 4 - TabStop = True - OnClick = radioStructureAndDataClick - end - object CheckListBoxFields: TCheckListBox - Left = 8 - Top = 157 - Width = 286 - Height = 108 - OnClickCheck = CheckListBoxFieldsClickCheck - Anchors = [akLeft, akTop, akBottom] - Columns = 2 - ItemHeight = 13 - TabOrder = 2 - end - object CheckBoxWithAllFields: TCheckBox - Left = 8 - Top = 133 - Width = 121 - Height = 17 - Caption = 'Include all columns' - Checked = True - State = cbChecked - TabOrder = 1 - OnClick = CheckBoxWithAllFieldsClick - end - object ButtonCancel: TButton - Left = 211 - Top = 296 + object btnCancel: TButton + Left = 273 + Top = 271 Width = 83 Height = 25 Anchors = [akRight, akBottom] Cancel = True Caption = 'Cancel' ModalResult = 2 - TabOrder = 5 + TabOrder = 4 end - object CheckBoxWithIndexes: TCheckBox + object comboDatabase: TComboBox Left = 8 - Top = 109 - Width = 121 - Height = 17 - Caption = 'Include indexes' - Checked = True - State = cbChecked - TabOrder = 6 - end - object ComboSelectDatabase: TComboBox - Left = 8 - Top = 72 - Width = 286 + Top = 24 + Width = 145 Height = 21 Style = csDropDownList - Anchors = [akLeft, akTop, akRight] - TabOrder = 7 + TabOrder = 0 end - object ButtonOK: TButton - Left = 122 - Top = 296 + object btnOK: TButton + Left = 184 + Top = 271 Width = 83 Height = 25 Anchors = [akRight, akBottom] Caption = 'OK' Default = True ModalResult = 1 - TabOrder = 8 - OnClick = ButtonOKClick + TabOrder = 5 + OnClick = btnOKClick end - object chkSelectAll: TCheckBox + object TreeElements: TVirtualStringTree Left = 8 - Top = 271 - Width = 286 - Height = 17 - Caption = 'Select / deselect all' - TabOrder = 9 - OnClick = chkSelectAllClick + Top = 70 + Width = 348 + Height = 88 + Anchors = [akLeft, akTop, akRight, akBottom] + Header.AutoSizeIndex = 0 + Header.DefaultHeight = 17 + Header.Font.Charset = DEFAULT_CHARSET + Header.Font.Color = clWindowText + Header.Font.Height = -11 + Header.Font.Name = 'Tahoma' + Header.Font.Style = [] + Header.MainColumn = -1 + Images = MainForm.ImageListMain + TabOrder = 2 + TreeOptions.AutoOptions = [toAutoDropExpand, toAutoScroll, toAutoScrollOnExpand, toAutoTristateTracking, toAutoDeleteMovedNodes] + 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 + Columns = <> + end + object MemoWhereClause: TSynMemo + Left = 8 + Top = 183 + Width = 348 + Height = 82 + SingleLineMode = False + Anchors = [akLeft, akRight, akBottom] + Font.Charset = DEFAULT_CHARSET + Font.Color = clGrayText + Font.Height = -13 + Font.Name = 'Courier New' + Font.Style = [] + TabOrder = 3 + Gutter.AutoSize = True + Gutter.DigitCount = 2 + Gutter.Font.Charset = DEFAULT_CHARSET + Gutter.Font.Color = clWindowText + Gutter.Font.Height = -11 + Gutter.Font.Name = 'Courier New' + Gutter.Font.Style = [] + Gutter.LeftOffset = 0 + Gutter.ShowLineNumbers = True + Highlighter = MainForm.SynSQLSyn1 + Options = [eoAutoIndent, eoAutoSizeMaxScrollWidth, eoDragDropEditing, eoEnhanceEndKey, eoGroupUndo, eoHideShowScrollbars, eoKeepCaretX, eoShowScrollHint, eoSmartTabDelete, eoSmartTabs, eoTabIndent] + WantTabs = True end end diff --git a/source/copytable.pas b/source/copytable.pas index 667f4a4b..122eb3eb 100644 --- a/source/copytable.pas +++ b/source/copytable.pas @@ -1,43 +1,44 @@ unit copytable; -// ------------------------------------- -// Copy table -// ------------------------------------- - - interface uses - Windows, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, CheckLst, - mysql_connection; + Windows, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, + mysql_connection, VirtualTrees, SynEdit, SynMemo; type TCopyTableForm = class(TForm) editNewTablename: TEdit; lblNewTablename: TLabel; - radioStructure: TRadioButton; - radioStructureAndData: TRadioButton; - CheckListBoxFields: TCheckListBox; - CheckBoxWithAllFields: TCheckBox; - ButtonCancel: TButton; - CheckBoxWithIndexes: TCheckBox; - lblTargetDB: TLabel; - ComboSelectDatabase: TComboBox; - ButtonOK: TButton; - chkSelectAll: TCheckBox; - procedure radioStructureClick(Sender: TObject); - procedure radioStructureAndDataClick(Sender: TObject); - procedure CheckBoxWithAllFieldsClick(Sender: TObject); + btnCancel: TButton; + comboDatabase: TComboBox; + btnOK: TButton; + TreeElements: TVirtualStringTree; + MemoWhereClause: TSynMemo; + lblItems: TLabel; + lblWhere: TLabel; procedure editNewTablenameChange(Sender: TObject); procedure FormShow(Sender: TObject); - procedure ButtonOKClick(Sender: TObject); + procedure btnOKClick(Sender: TObject); procedure FormCreate(Sender: TObject); - procedure CheckListBoxFieldsClickCheck(Sender: TObject); - procedure chkSelectAllClick(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 FormDestroy(Sender: TObject); + procedure TreeElementsChecked(Sender: TBaseVirtualTree; Node: PVirtualNode); + procedure FormClose(Sender: TObject; var Action: TCloseAction); private { Private declarations } - oldTableName : String; + FOrgTableName: String; + FColumns: TTableColumnList; + FKeys: TTableKeyList; + FForeignKeys: TForeignKeyList; public { Public declarations } end; @@ -47,295 +48,288 @@ implementation uses helpers, main; +const + nColumns = 0; + nKeys = 1; + nForeignKeys = 2; + nData = 3; + {$R *.DFM} +{$I const.inc} -procedure TCopyTableForm.radioStructureClick(Sender: TObject); -begin - radioStructureAndData.Checked := not radioStructure.Checked; -end; - -procedure TCopyTableForm.radioStructureAndDataClick(Sender: TObject); -begin - radioStructure.Checked := not radioStructureAndData.Checked; -end; - -procedure TCopyTableForm.CheckBoxWithAllFieldsClick(Sender: TObject); -begin - CheckListBoxFields.Enabled := not CheckBoxWithAllFields.Checked; - chkSelectAll.Enabled := CheckListBoxFields.Enabled; -end; - - -procedure TCopyTableForm.editNewTablenameChange(Sender: TObject); -begin - // validate tablename - try - ensureValidIdentifier( editNewTablename.Text ); - editNewTablename.Font.Color := clWindowText; - editNewTablename.Color := clWindow; - // Enable "OK"-Button if we have a valid name - ButtonOK.Enabled := True; - except - editNewTablename.Font.Color := clRed; - editNewTablename.Color := clYellow; - ButtonOK.Enabled := False; - end; -end; - - procedure TCopyTableForm.FormCreate(Sender: TObject); begin InheritFont(Font); + Width := GetRegValue(REGNAME_COPYTABLE_WINWIDTH, Width); + Height := GetRegValue(REGNAME_COPYTABLE_WINHEIGHT, Height); + SetWindowSizeGrip(Handle, True); + MainForm.SetupSynEditors; + FixVT(TreeElements); + FColumns := TTableColumnList.Create; + FKeys := TTableKeyList.Create; + FForeignKeys := TForeignKeyList.Create; +end; + + +procedure TCopyTableForm.FormDestroy(Sender: TObject); +begin + // Save GUI stuff + MainReg.WriteInteger(REGNAME_COPYTABLE_WINWIDTH, Width); + MainReg.WriteInteger(REGNAME_COPYTABLE_WINHEIGHT, Height); end; procedure TCopyTableForm.FormShow(Sender: TObject); var - i : Integer; - struc_data : Byte; + CreateCode: String; begin if Mainform.DBtree.Focused then - oldTableName := Mainform.SelectedTable.Name + FOrgTableName := Mainform.SelectedTable.Name else - oldTableName := Mainform.ListTables.Text[Mainform.ListTables.FocusedNode, 0]; - editNewTablename.Text := oldTableName + '_copy'; + FOrgTableName := Mainform.ListTables.Text[Mainform.ListTables.FocusedNode, 0]; + editNewTablename.Text := FOrgTableName + '_copy'; + editNewTablename.SetFocus; + lblNewTablename.Caption := 'Copy ''' + FOrgTableName + ''' to new db.table:'; editNewTablename.SetFocus; - lblNewTablename.Caption := 'Copy ''' + oldTableName + ''' to new table:'; // Select TargetDatabase - ComboSelectDatabase.Items.Clear; - ComboSelectDatabase.Items.Assign(Mainform.AllDatabases); - ComboSelectDatabase.ItemIndex := ComboSelectDatabase.Items.IndexOf( Mainform.ActiveDatabase ); - if comboSelectDatabase.ItemIndex = -1 then - comboSelectDatabase.ItemIndex := 0; + comboDatabase.Items.Clear; + comboDatabase.Items.Assign(Mainform.AllDatabases); + comboDatabase.ItemIndex := comboDatabase.Items.IndexOf(Mainform.ActiveDatabase); + if comboDatabase.ItemIndex = -1 then + comboDatabase.ItemIndex := 0; - // fill columns: - CheckListBoxFields.Items.Text := Mainform.Connection.GetCol('SHOW FIELDS FROM ' + mainform.mask(oldTableName)).Text; + CreateCode := MainForm.Connection.GetVar('SHOW CREATE TABLE '+MainForm.mask(FOrgTableName), 1); + FColumns.Clear; + FKeys.Clear; + FForeignKeys.Clear; + ParseTableStructure(CreateCode, FColumns, FKeys, FForeignKeys); - // select all: - for i:=0 to CheckListBoxFields.Items.Count-1 do - CheckListBoxFields.checked[i] := true; - - {*** - restore last settings - @see feature #1647058 - } - struc_data := GetRegValue( REGNAME_COPYTABLE_STRUCDATA, DEFAULT_COPYTABLE_STRUCDATA ); - case struc_data of - REGVAL_COPYTABLE_STRUCTURE: - radioStructure.Checked := true; - REGVAL_COPYTABLE_STRUCTURE_AND_DATA: - radioStructureAndData.Checked := true; - end; - CheckBoxWithIndexes.Checked := GetRegValue( REGNAME_COPYTABLE_INDEXES, CheckBoxWithIndexes.Checked ); - CheckBoxWithAllFields.Checked := GetRegValue( REGNAME_COPYTABLE_ALLFIELDS, CheckBoxWithAllFields.Checked ); - // Ensure CheckListBoxFields + chkSelectAll are en/disabled - CheckBoxWithAllFieldsClick(Sender); - // Ensure chkSelectAll shows its correct state - CheckListBoxFieldsClickCheck(Sender); + TreeElements.Clear; + TreeElements.RootNodeCount := 4; end; -procedure TCopyTableForm.ButtonOKClick(Sender: TObject); + +procedure TCopyTableForm.FormClose(Sender: TObject; var Action: TCloseAction); var - strquery : String; - i,which,k : Integer; - keylist : Array of TMyKey; - keystr : String; - notnull, - default : String; - Results : TMySQLQuery; - isFulltext : Boolean; - struc_data : Byte; - Fixes : TStringList; - DBObjects : TDBObjectList; + Node: PVirtualNode; + Option: String; begin - // copy table! - - // store settings - if radioStructure.Checked then struc_data := REGVAL_COPYTABLE_STRUCTURE - else struc_data := REGVAL_COPYTABLE_STRUCTURE_AND_DATA; - - OpenRegistry; - MainReg.WriteInteger( REGNAME_COPYTABLE_STRUCDATA, struc_data ); - MainReg.WriteBool( REGNAME_COPYTABLE_INDEXES, CheckBoxWithIndexes.Checked ); - MainReg.WriteBool( REGNAME_COPYTABLE_ALLFIELDS, CheckBoxWithAllFields.Checked ); - - strquery := 'CREATE TABLE ' + mainform.mask(ComboSelectDatabase.Text) + '.' + mainform.mask(editNewTablename.Text) + ' '; - - // keys > - if CheckBoxWithIndexes.Checked then begin - Results := Mainform.Connection.GetResults('SHOW KEYS FROM ' + mainform.mask(oldtablename)); - setLength(keylist, 0); - keystr := ''; - - for i:=1 to Results.RecordCount do - begin - which := -1; - - for k:=0 to length(keylist)-1 do - begin - if keylist[k].Name = Results.Col(2) then // keyname exists! - which := k; - end; - if which = -1 then - begin - setlength(keylist, length(keylist)+1); - which := high(keylist); - keylist[which].Columns := TStringList.Create; - keylist[which].SubParts := TStringList.Create; - // set properties for new key - if Mainform.Connection.ServerVersionInt < 40002 then - isFulltext := Results.Col('Comment') = 'FULLTEXT' - else - isFulltext := Results.Col('Index_type') = 'FULLTEXT'; - keylist[which].Name := Results.Col(2); - if Results.Col(2) = 'PRIMARY' then - keylist[which]._type := 'PRIMARY' - else if isFulltext then - keylist[which]._type := 'FULLTEXT' - else if Results.Col(1) = '1' then - keylist[which]._type := '' - else if Results.Col(1) = '0' then - keylist[which]._type := 'UNIQUE'; - end; - // add column - keylist[which].Columns.add(Results.Col('Column_name')); - keylist[which].SubParts.add(Results.Col('Sub_part')); - Results.Next; + // Save first level node check options + Node := TreeElements.GetFirst; + while Assigned(Node) do begin + case Node.Index of + nColumns: Option := REGNAME_COPYTABLE_COLUMNS; + nKeys: Option := REGNAME_COPYTABLE_KEYS; + nForeignKeys: Option := REGNAME_COPYTABLE_FOREIGN; + nData: Option := REGNAME_COPYTABLE_DATA; + else raise Exception.Create(SUnhandledNodeIndex); end; - FreeAndNil(Results); - for k:=0 to high(keylist) do - begin - if k > 0 then - keystr := keystr + ','; - if keylist[k].Name = 'PRIMARY' then - keystr := keystr + ' PRIMARY KEY (' - else - keystr := keystr + ' ' + keylist[k]._type + ' KEY ' + Mainform.Mask(keylist[k].Name) + ' ('; - for i := 0 to keylist[k].Columns.count - 1 do - begin - if i > 0 then - keystr := keystr + ', '; - keystr := keystr + mainform.mask(keylist[k].Columns[i]); - if keylist[k].SubParts[i] <> '' then - keystr := keystr + '(' + keylist[k].SubParts[i] + ')'; - end; - keystr := keystr + ')'; - end; - if keystr<> '' then - strquery := strquery + '(' + keystr + ')' + MainReg.WriteBool(Option, Node.CheckState in [csCheckedNormal, csCheckedPressed]); + Node := TreeElements.GetNextSibling(Node); end; - // < keys +end; + + +procedure TCopyTableForm.TreeElementsChecked(Sender: TBaseVirtualTree; Node: PVirtualNode); +begin + // Disable WHERE memo if "Data" was unselected + if (Node.Index = nData) then begin + MemoWhereClause.Enabled := Node.CheckState = csCheckedNormal; + if MemoWhereClause.Enabled then begin + MemoWhereClause.Highlighter := MainForm.SynSQLSyn1; + MemoWhereClause.Color := clWindow; + end else begin + MemoWhereClause.Highlighter := nil; + MemoWhereClause.Color := clBtnFace; + end; + end; +end; + + +procedure TCopyTableForm.TreeElementsGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; + Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer); +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 := GetIndexIcon(FKeys[Node.Index].IndexType); + 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); +begin + // Get node text + case Sender.GetNodeLevel(Node) of + 0: case Node.Index of + nColumns: CellText := 'Columns'; + nKeys: CellText := 'Indexes'; + nForeignKeys: CellText := 'Foreign keys'; + nData: CellText := 'Data'; + else raise Exception.Create(SUnhandledNodeIndex); + 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: String; +begin + // First three upper nodes mostly have child nodes + Node.CheckType := ctTriStateCheckBox; + case Sender.GetNodeLevel(Node) of + 0: begin + if Node.Index in [nColumns, nKeys, nForeignKeys] then + Include(InitialStates, ivsHasChildren); + case Node.Index of + nColumns: Option := REGNAME_COPYTABLE_COLUMNS; + nKeys: Option := REGNAME_COPYTABLE_KEYS; + nForeignKeys: Option := REGNAME_COPYTABLE_FOREIGN; + nData: Option := REGNAME_COPYTABLE_DATA; + else raise Exception.Create(SUnhandledNodeIndex); + end; + if GetRegValue(Option, True) then + Node.CheckState := csCheckedNormal; + (Sender as TVirtualStringTree).OnChecked(Sender, Node); + end; + + 1: if Node.Parent.CheckState in [csCheckedNormal, csCheckedPressed, csMixedNormal, csMixedPressed] then + Node.CheckState := csCheckedNormal; + end; +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, Clause, DataCols: String; + ParentNode, Node: PVirtualNode; + DBObjects: TDBObjectList; + Obj: TDBObject; + DoData: Boolean; +begin + // Compose and run CREATE query + Screen.Cursor := crHourglass; + MainForm.ShowStatusMsg('Generating SQL code ...'); + DataCols := ''; + DoData := False; + ParentNode := TreeElements.GetFirst; + while Assigned(ParentNode) do begin + Node := TreeElements.GetFirstChild(ParentNode); + while Assigned(Node) do begin + if Node.CheckState in [csCheckedNormal, csCheckedPressed] then begin + case ParentNode.Index of + nColumns: begin + Clause := FColumns[Node.Index].SQLCode; + DataCols := DataCols + MainForm.mask(FColumns[Node.Index].Name) + ', '; + end; + nKeys: Clause := FKeys[Node.Index].SQLCode; + nForeignkeys: Clause := FForeignKeys[Node.Index].SQLCode; + else raise Exception.Create(SUnhandledNodeIndex); + end; + CreateCode := CreateCode + #9 + Clause + ',' + CRLF; + end; + Node := TreeElements.GetNextSibling(Node); + end; + if (ParentNode.Index = nData) then + DoData := ParentNode.CheckState in [csCheckedNormal, csCheckedPressed]; + ParentNode := TreeElements.GetNextSibling(ParentNode); + end; + Delete(CreateCode, Length(CreateCode)-2, 3); + CreateCode := 'CREATE TABLE '+Mainform.mask(comboDatabase.Text)+'.'+Mainform.mask(editNewTablename.Text)+' ('+CRLF+CreateCode+CRLF+')'+CRLF; // Add collation and engine clauses DBObjects := Mainform.Connection.GetDBObjects(Mainform.ActiveDatabase); - for i:=0 to DBObjects.Count-1 do begin - if DBObjects[i].Name = oldTableName then begin - if DBObjects[i].Collation <> '' then - strquery := strquery + ' COLLATE ' + DBObjects[i].Collation; - if DBObjects[i].Engine <> '' then begin + for Obj in DBObjects do begin + if Obj.Name = FOrgTableName then begin + if Obj.Collation <> '' then + CreateCode := CreateCode + ' COLLATE ''' + Obj.Collation + ''''; + if Obj.Engine <> '' then begin if Mainform.Connection.ServerVersionInt < 40018 then - strquery := strquery + ' TYPE=' + DBObjects[i].Engine + CreateCode := CreateCode + ' TYPE=' + Obj.Engine else - strquery := strquery + ' ENGINE=' + DBObjects[i].Engine; + CreateCode := CreateCode + ' ENGINE=' + Obj.Engine; end; - strquery := strquery + ' COMMENT=' + esc(DBObjects[i].Comment); + if Obj.RowFormat <> '' then + CreateCode := CreateCode + ' ROW_FORMAT=' + Obj.RowFormat; + if Obj.AutoInc > -1 then + CreateCode := CreateCode + ' AUTO_INCREMENT=' + IntToStr(Obj.AutoInc); + CreateCode := CreateCode + ' COMMENT=' + esc(Obj.Comment); break; end; end; - strquery := strquery + ' SELECT'; - - // which fields? - if CheckBoxWithAllFields.Checked then - strquery := strquery + ' *' - else begin - for i:=0 to CheckListBoxFields.Items.Count-1 do - if CheckListBoxFields.Checked[i] then - strquery := strquery + ' ' + mainform.mask(CheckListBoxFields.Items[i]) + ','; - delete(strquery, length(strquery), 1); + // Append SELECT .. FROM OrgTable clause + if DoData and (DataCols <> '') then begin + DataCols := Trim(DataCols); + Delete(DataCols, Length(DataCols), 1); + CreateCode := CreateCode + ' SELECT ' + DataCols + ' FROM ' + MainForm.mask(FOrgTableName); + if MemoWhereClause.GetTextLen > 0 then + CreateCode := CreateCode + ' WHERE ' + MemoWhereClause.Text; end; - strquery := strquery + ' FROM ' + mainform.mask(oldTableName); - - // what? - if radioStructure.Checked then - strquery := strquery + ' WHERE 1 = 0'; - + // Run query and refresh list try - Mainform.Connection.Query(strquery, False); - - // Fix missing auto_increment property and CURRENT_TIMESTAMP defaults in new table - Results := Mainform.Connection.GetResults('SHOW FIELDS FROM ' + mainform.mask(oldtablename)); - Fixes := TStringList.Create; - while not Results.Eof do begin - notnull := ''; - if Results.Col('Null') = '' then - notnull := 'NOT NULL'; - default := ''; - if Results.Col('Default') <> '' then begin - default := 'DEFAULT '; - if Results.Col('Default') = 'CURRENT_TIMESTAMP' then - default := default + Results.Col('Default') - else - default := default + esc(Results.Col('Default')); - end; - - if (CheckBoxWithIndexes.Checked and (Results.Col('Extra') = 'auto_increment')) - or (Results.Col('Default') = 'CURRENT_TIMESTAMP') then begin - Fixes.Add('CHANGE '+Mainform.mask(Results.Col('Field'))+' '+ - Mainform.mask(Results.Col('Field'))+' '+ - Results.Col('Type')+' '+default+' '+notnull+' '+Results.Col('Extra')); - end; - - Results.Next; - end; - if Fixes.Count > 0 then begin - Mainform.Connection.Query('ALTER TABLE '+Mainform.mask(ComboSelectDatabase.Text) + '.'+Mainform.mask(editNewTablename.Text)+ ' '+ - ImplodeStr(', ', Fixes) - ); - end; - Results.Free; - FreeAndNil(Fixes); - Mainform.actRefresh.Execute; + MainForm.ShowStatusMsg('Creating table ...'); + MainForm.Connection.Query(CreateCode); + MainForm.actRefresh.Execute; except on E:EDatabaseError do begin + Screen.Cursor := crDefault; MessageDlg(E.Message, mtError, [mbOk], 0); ModalResult := mrNone; end; end; -end; - -procedure TCopyTableForm.CheckListBoxFieldsClickCheck(Sender: TObject); -var - i : Integer; - allSelected, noneSelected : Boolean; -begin - allselected := True; - noneSelected := True; - for i := 0 to CheckListBoxFields.Items.Count - 1 do begin - if CheckListBoxFields.Checked[i] then - noneSelected := False - else - allSelected := False; - end; - if noneSelected then - chkSelectAll.State := cbUnchecked - else if allSelected then - chkSelectAll.State := cbChecked - else - chkSelectAll.State := cbGrayed; -end; - - -procedure TCopyTableForm.chkSelectAllClick(Sender: TObject); -begin - // Avoid executing when checkbox was toggled by code (see proc below) - if (Sender as TCheckBox).Focused then - ToggleCheckListBox( CheckListBoxFields, (Sender as TCheckBox).Checked ); + MainForm.ShowStatusMsg; + Screen.Cursor := crDefault; end; end. diff --git a/source/grideditlinks.pas b/source/grideditlinks.pas index e67bb071..f4077cb9 100644 --- a/source/grideditlinks.pas +++ b/source/grideditlinks.pas @@ -186,8 +186,6 @@ type procedure SetBounds(R: TRect); override; end; -function GetColumnDefaultType(var Text: String): TColumnDefaultType; -function GetColumnDefaultClause(DefaultType: TColumnDefaultType; Text: String): String; implementation @@ -1245,28 +1243,6 @@ begin end; -function GetColumnDefaultType(var Text: String): TColumnDefaultType; -begin - Result := TColumnDefaultType(MakeInt(Copy(Text, 1, 1))); - Text := Copy(Text, 2, Length(Text)-1); -end; - - -function GetColumnDefaultClause(DefaultType: TColumnDefaultType; Text: String): String; -begin - case DefaultType of - cdtNothing: Result := ''; - cdtText: Result := 'DEFAULT '+esc(Text); - cdtTextUpdateTS: Result := 'DEFAULT '+esc(Text)+' ON UPDATE CURRENT_TIMESTAMP'; - cdtNull: Result := 'DEFAULT NULL'; - cdtNullUpdateTS: Result := 'DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP'; - cdtCurTS: Result := 'DEFAULT CURRENT_TIMESTAMP'; - cdtCurTSUpdateTS: Result := 'DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'; - cdtAutoInc: Result := 'AUTO_INCREMENT'; - end; -end; - - { Datatype selector } constructor TDataTypeEditorLink.Create(Tree: TVirtualStringTree); diff --git a/source/helpers.pas b/source/helpers.pas index 36524800..abb7c041 100644 --- a/source/helpers.pas +++ b/source/helpers.pas @@ -39,13 +39,6 @@ type TLineBreaks = (lbsNone, lbsWindows, lbsUnix, lbsMac, lbsWide, lbsMixed); - TMyKey = record - Name : String; - _type : String; - Columns : TStringList; - SubParts : TStringList; - end; - TDBObjectEditor = class(TFrame) private FModified: Boolean; @@ -171,6 +164,8 @@ type procedure HandlePortableSettings(StartupMode: Boolean); function LoadConnectionParams(Session: String): TConnectionParameters; function CompareAnyNode(Text1, Text2: String): Integer; + function GetColumnDefaultType(var Text: String): TColumnDefaultType; + function GetColumnDefaultClause(DefaultType: TColumnDefaultType; Text: String): String; var MainReg: TRegistry; @@ -3368,6 +3363,30 @@ begin end; end; + +function GetColumnDefaultType(var Text: String): TColumnDefaultType; +begin + Result := TColumnDefaultType(MakeInt(Copy(Text, 1, 1))); + Text := Copy(Text, 2, Length(Text)-1); +end; + + +function GetColumnDefaultClause(DefaultType: TColumnDefaultType; Text: String): String; +begin + case DefaultType of + cdtNothing: Result := ''; + cdtText: Result := 'DEFAULT '+esc(Text); + cdtTextUpdateTS: Result := 'DEFAULT '+esc(Text)+' ON UPDATE CURRENT_TIMESTAMP'; + cdtNull: Result := 'DEFAULT NULL'; + cdtNullUpdateTS: Result := 'DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP'; + cdtCurTS: Result := 'DEFAULT CURRENT_TIMESTAMP'; + cdtCurTSUpdateTS: Result := 'DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'; + cdtAutoInc: Result := 'AUTO_INCREMENT'; + end; +end; + + + end. diff --git a/source/main.pas b/source/main.pas index b7d51ef8..fb3c7b48 100644 --- a/source/main.pas +++ b/source/main.pas @@ -949,7 +949,6 @@ type procedure SetWindowCaption; procedure OnMessageHandler(var Msg: TMsg; var Handled: Boolean); procedure DefaultHandler(var Message); override; - function MaskMulti(str: String): String; procedure SelectDBObject(Text: String; NodeType: TListNodeType); procedure SetupSynEditors; procedure ParseSelectedTableStructure; @@ -1120,6 +1119,7 @@ begin FreeAndNil(SessionManager); FreeAndNil(CreateDatabaseForm); FreeAndNil(SearchReplaceDialog); + FreeAndNil(CopyTableDialog); // Close database connection DoDisconnect; @@ -2056,21 +2056,6 @@ begin end; -// Quote identifier, probably with multiple segments, e.g. db.table.column -function TMainform.MaskMulti(str: String): String; -var - Segments: TStringList; - i: Integer; -begin - Segments := Explode('.', str); - Result := ''; - for i:=0 to Segments.Count-1 do - Result := Result + mask(Segments[i]) + '.'; - FreeAndNil(Segments); - Delete(Result, Length(Result), 1); -end; - - procedure TMainForm.actExportSettingsExecute(Sender: TObject); begin // Export settings to .reg-file @@ -7218,7 +7203,7 @@ begin idx := ForeignKey.Columns.IndexOf(DataGrid.Header.Columns[Column].Text); if idx > -1 then begin // Find the first text column if available and use that for displaying in the pulldown instead of using meaningless id numbers - CreateTable := Connection.GetVar('SHOW CREATE TABLE '+MaskMulti(ForeignKey.ReferenceTable), 1); + CreateTable := Connection.GetVar('SHOW CREATE TABLE '+Mask(ForeignKey.ReferenceTable), 1); Columns := TTableColumnList.Create; Keys := nil; ForeignKeys := nil; @@ -7234,7 +7219,7 @@ begin KeyCol := Mask(ForeignKey.ForeignColumns[idx]); SQL := 'SELECT '+KeyCol; if TextCol <> '' then SQL := SQL + ', LEFT(' + Mask(TextCol) + ', 256)'; - SQL := SQL + ' FROM '+MaskMulti(ForeignKey.ReferenceTable)+' GROUP BY '+KeyCol+' ORDER BY '; + SQL := SQL + ' FROM '+Mask(ForeignKey.ReferenceTable)+' GROUP BY '+KeyCol+' ORDER BY '; if TextCol <> '' then SQL := SQL + Mask(TextCol) else SQL := SQL + KeyCol; SQL := SQL + ' LIMIT 1000'; @@ -8852,6 +8837,8 @@ begin Editors.Add(SQLHelpForm.memoDescription); Editors.Add(SQLHelpForm.MemoExample); end; + if Assigned(CopyTableDialog) then + Editors.Add(CopyTableDialog.MemoWhereClause); FontName := GetRegValue(REGNAME_FONTNAME, DEFAULT_FONTNAME); FontSize := GetRegValue(REGNAME_FONTSIZE, DEFAULT_FONTSIZE); diff --git a/source/mysql_connection.pas b/source/mysql_connection.pas index cfcd7569..85e3636c 100644 --- a/source/mysql_connection.pas +++ b/source/mysql_connection.pas @@ -61,6 +61,7 @@ type FStatus: TEditingStatus; constructor Create; destructor Destroy; override; + function SQLCode: String; property Status: TEditingStatus read FStatus write SetStatus; end; PTableColumn = ^TTableColumn; @@ -75,6 +76,7 @@ type constructor Create; destructor Destroy; override; procedure Modification(Sender: TObject); + function SQLCode: String; end; TTableKeyList = TObjectList; @@ -86,6 +88,7 @@ type Modified, Added, KeyNameWasCustomized: Boolean; constructor Create; destructor Destroy; override; + function SQLCode: String; end; TForeignKeyList = TObjectList; @@ -233,7 +236,7 @@ type function Query(SQL: String; DoStoreResult: Boolean=False; LogCategory: TMySQLLogCategory=lcSQL): PMYSQL_RES; function EscapeString(Text: String; ProcessJokerChars: Boolean=False): String; function escChars(const Text: String; EscChar, Char1, Char2, Char3, Char4: Char): String; - function QuoteIdent(Identifier: String): String; + class function QuoteIdent(Identifier: String): String; function DeQuoteIdent(Identifier: String): String; function ConvertServerVersion(Version: Integer): String; function GetResults(SQL: String): TMySQLQuery; @@ -943,9 +946,11 @@ end; Add backticks to identifier Todo: Support ANSI style } -function TMySQLConnection.QuoteIdent(Identifier: String): String; +class function TMySQLConnection.QuoteIdent(Identifier: String): String; begin - Result := StringReplace(Identifier, '`', '``', [rfReplaceAll]); + Result := Identifier; + Result := StringReplace(Result, '`', '``', [rfReplaceAll]); + Result := StringReplace(Result, '.', '`.`', [rfReplaceAll]); Result := '`' + Result + '`'; end; @@ -2424,6 +2429,26 @@ begin FStatus := Value; end; +function TTableColumn.SQLCode: String; +begin + Result := TMySQLConnection.QuoteIdent(Name) + ' ' +DataType.Name; + if LengthSet <> '' then + Result := Result + '(' + LengthSet + ')'; + if DataType.HasUnsigned and Unsigned then + Result := Result + ' UNSIGNED'; + if not AllowNull then + Result := Result + ' NOT'; + Result := Result + ' NULL'; + if DefaultType <> cdtNothing then begin + Result := Result + ' ' + GetColumnDefaultClause(DefaultType, DefaultText); + Result := TrimRight(Result); // Remove whitespace for columns without default value + end; + if Comment <> '' then + Result := Result + ' COMMENT '+esc(Comment); + if Collation <> '' then + Result := Result + ' COLLATE '+esc(Collation); +end; + { *** TTableKey } @@ -2450,6 +2475,39 @@ begin Modified := True; end; +function TTableKey.SQLCode: String; +var + i: Integer; +begin + Result := ''; + // Supress SQL error trying index creation with 0 column + if Columns.Count = 0 then + Exit; + if IndexType = PKEY then + Result := Result + 'PRIMARY KEY ' + else begin + if IndexType <> KEY then + Result := Result + IndexType + ' '; + Result := Result + 'INDEX ' + TMySQLConnection.QuoteIdent(Name) + ' '; + end; + Result := Result + '('; + for i:=0 to Columns.Count-1 do begin + Result := Result + TMySQLConnection.QuoteIdent(Columns[i]); + if SubParts[i] <> '' then + Result := Result + '(' + SubParts[i] + ')'; + Result := Result + ', '; + end; + if Columns.Count > 0 then + Delete(Result, Length(Result)-1, 2); + + Result := Result + ')'; + + if Algorithm <> '' then + Result := Result + ' USING ' + Algorithm; +end; + + + { *** TForeignKey } @@ -2467,6 +2525,25 @@ begin inherited Destroy; end; +function TForeignKey.SQLCode: String; +var + i: Integer; +begin + Result := 'CONSTRAINT '+TMySQLConnection.QuoteIdent(KeyName)+' FOREIGN KEY ('; + for i:=0 to Columns.Count-1 do + Result := Result + TMySQLConnection.QuoteIdent(Columns[i]) + ', '; + if Columns.Count > 0 then Delete(Result, Length(Result)-1, 2); + Result := Result + ') REFERENCES ' + TMySQLConnection.QuoteIdent(ReferenceTable) + ' ('; + for i:=0 to ForeignColumns.Count-1 do + Result := Result + TMySQLConnection.QuoteIdent(ForeignColumns[i]) + ', '; + if ForeignColumns.Count > 0 then Delete(Result, Length(Result)-1, 2); + Result := Result + ')'; + if OnUpdate <> '' then + Result := Result + ' ON UPDATE ' + OnUpdate; + if OnDelete <> '' then + Result := Result + ' ON DELETE ' + OnDelete; +end; + end. diff --git a/source/table_editor.pas b/source/table_editor.pas index 70d5b586..652daf38 100644 --- a/source/table_editor.pas +++ b/source/table_editor.pas @@ -189,8 +189,6 @@ type procedure ResetModificationFlags; function ComposeCreateStatement: String; function ComposeAlterStatement: String; - function GetIndexSQL(idx: Integer): String; - function GetForeignKeySQL(idx: Integer): String; procedure UpdateSQLcode; function CellEditingAllowed(Node: PVirtualNode; Column: TColumnIndex): Boolean; public @@ -572,14 +570,14 @@ begin Specs.Add('DROP '+IndexSQL); end; if FKeys[i].Added or FKeys[i].Modified then - Specs.Add('ADD '+GetIndexSQL(i)); + Specs.Add('ADD '+FKeys[i].SQLCode); end; for i:=0 to DeletedForeignKeys.Count-1 do Specs.Add('DROP FOREIGN KEY '+Mainform.mask(DeletedForeignKeys[i])); for i:=0 to FForeignKeys.Count-1 do begin if FForeignKeys[i].Added or FForeignKeys[i].Modified then - Specs.Add('ADD '+GetForeignKeySQL(i)); + Specs.Add('ADD '+FForeignKeys[i].SQLCode); end; Result := 'ALTER TABLE '+Mainform.mask(DBObject.Name) + CRLF + #9 + ImplodeStr(',' + CRLF + #9, Specs); @@ -603,29 +601,13 @@ begin Node := listColumns.GetFirst; while Assigned(Node) do begin Col := listColumns.GetNodeData(Node); - Result := Result + #9 + Mainform.mask(Col.Name) + ' ' +Col.DataType.Name; - if Col.LengthSet <> '' then - Result := Result + '(' + Col.LengthSet + ')'; - if Col.DataType.HasUnsigned and Col.Unsigned then - Result := Result + ' UNSIGNED'; - if not Col.AllowNull then - Result := Result + ' NOT'; - Result := Result + ' NULL'; - if Col.DefaultType <> cdtNothing then begin - Result := Result + ' ' + GetColumnDefaultClause(Col.DefaultType, Col.DefaultText); - Result := TrimRight(Result); // Remove whitespace for columns without default value - end; - if Col.Comment <> '' then - Result := Result + ' COMMENT '+esc(Col.Comment); - if Col.Collation <> '' then - Result := Result + ' COLLATE '+esc(Col.Collation); - Result := Result + ','+CRLF; + Result := Result + #9 + Col.SQLCode + ','+CRLF; Node := listColumns.GetNextSibling(Node); end; IndexCount := 0; for i:=0 to FKeys.Count-1 do begin - tmp := GetIndexSQL(i); + tmp := FKeys[i].SQLCode; if tmp <> '' then begin Result := Result + #9 + tmp + ','+CRLF; Inc(IndexCount); @@ -633,7 +615,7 @@ begin end; for i:=0 to FForeignKeys.Count-1 do - Result := Result + #9 + GetForeignKeySQL(i) + ','+CRLF; + Result := Result + #9 + FForeignKeys[i].SQLCode + ','+CRLF; if Integer(listColumns.RootNodeCount) + IndexCount + FForeignKeys.Count > 0 then Delete(Result, Length(Result)-2, 3); @@ -667,60 +649,6 @@ begin end; -function TfrmTableEditor.GetIndexSQL(idx: Integer): String; -var - i: Integer; -begin - Result := ''; - // Supress SQL error trying index creation with 0 column - if FKeys[idx].Columns.Count = 0 then - Exit; - if FKeys[idx].IndexType = PKEY then - Result := Result + 'PRIMARY KEY ' - else begin - if FKeys[idx].IndexType <> KEY then - Result := Result + FKeys[idx].IndexType + ' '; - Result := Result + 'INDEX ' + Mainform.Mask(FKeys[idx].Name) + ' '; - end; - Result := Result + '('; - for i:=0 to FKeys[idx].Columns.Count-1 do begin - Result := Result + Mainform.Mask(FKeys[idx].Columns[i]); - if FKeys[idx].SubParts[i] <> '' then - Result := Result + '(' + FKeys[idx].SubParts[i] + ')'; - Result := Result + ', '; - end; - if FKeys[idx].Columns.Count > 0 then - Delete(Result, Length(Result)-1, 2); - - Result := Result + ')'; - - if FKeys[idx].Algorithm <> '' then - Result := Result + ' USING ' + FKeys[idx].Algorithm; -end; - - -function TfrmTableEditor.GetForeignKeySQL(idx: Integer): String; -var - Key: TForeignKey; - i: Integer; -begin - Key := FForeignKeys[idx]; - Result := 'CONSTRAINT '+Mainform.mask(Key.KeyName)+' FOREIGN KEY ('; - for i:=0 to Key.Columns.Count-1 do - Result := Result + Mainform.mask(Key.Columns[i]) + ', '; - if Key.Columns.Count > 0 then Delete(Result, Length(Result)-1, 2); - Result := Result + ') REFERENCES ' + Mainform.MaskMulti(Key.ReferenceTable) + ' ('; - for i:=0 to Key.ForeignColumns.Count-1 do - Result := Result + Mainform.mask(Key.ForeignColumns[i]) + ', '; - if Key.ForeignColumns.Count > 0 then Delete(Result, Length(Result)-1, 2); - Result := Result + ')'; - if Key.OnUpdate <> '' then - Result := Result + ' ON UPDATE ' + Key.OnUpdate; - if Key.OnDelete <> '' then - Result := Result + ' ON DELETE ' + Key.OnDelete; -end; - - procedure TfrmTableEditor.editNameChange(Sender: TObject); begin // Name edited @@ -1994,7 +1922,7 @@ begin MessageDlg('Please select a reference table before selecting foreign columns.', mtError, [mbOk], 0) else begin try - Mainform.Connection.GetVar('SELECT 1 FROM '+Mainform.MaskMulti(Key.ReferenceTable)); + Mainform.Connection.GetVar('SELECT 1 FROM '+Mainform.Mask(Key.ReferenceTable)); Allowed := True; except // Leave Allowed = False @@ -2046,7 +1974,7 @@ begin 3: begin Key := FForeignKeys[Node.Index]; SetEditor := TSetEditorLink.Create(VT); - SetEditor.ValueList := Mainform.Connection.GetCol('SHOW COLUMNS FROM '+Mainform.MaskMulti(Key.ReferenceTable)); + SetEditor.ValueList := Mainform.Connection.GetCol('SHOW COLUMNS FROM '+Mainform.Mask(Key.ReferenceTable)); EditLink := SetEditor; end; 4, 5: begin