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.
This commit is contained in:
Ansgar Becker
2010-06-14 00:21:33 +00:00
parent 15d73678c6
commit c0da6e5a21
8 changed files with 474 additions and 491 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<TTableKey>;
@ -86,6 +88,7 @@ type
Modified, Added, KeyNameWasCustomized: Boolean;
constructor Create;
destructor Destroy; override;
function SQLCode: String;
end;
TForeignKeyList = TObjectList<TForeignKey>;
@ -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.

View File

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