mirror of
https://github.com/HeidiSQL/HeidiSQL.git
synced 2025-08-06 18:24:26 +08:00
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:
@ -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';
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user