diff --git a/heidisql.lpi b/heidisql.lpi
index a952a1f0..192e9df7 100644
--- a/heidisql.lpi
+++ b/heidisql.lpi
@@ -314,6 +314,13 @@
+
+
+
+
+
+
+
diff --git a/heidisql.lpr b/heidisql.lpr
index f3e0c359..eaac72c9 100644
--- a/heidisql.lpr
+++ b/heidisql.lpr
@@ -21,7 +21,7 @@ uses
exportgrid, usermanager, selectdbobject, reformatter, searchreplace,
connections, jsonregistry, sqlhelp, updatecheck, insertfiles, texteditor,
customize_highlighter, preferences, table_editor, view, routine_editor,
- trigger_editor, event_editor;
+ trigger_editor, event_editor, tabletools;
{$R *.res}
{.$R resources.rc}
diff --git a/source/apphelpers.pas b/source/apphelpers.pas
index e20994d7..8e4130be 100644
--- a/source/apphelpers.pas
+++ b/source/apphelpers.pas
@@ -164,9 +164,9 @@ type
procedure LogFromThread(Msg: String; Category: TDBLogCategory);
end;
- {TSqlTranspiler = class(TObject)
+ TSqlTranspiler = class(TObject)
class function CreateTable(SQL: String; SourceDb, TargetDb: TDBConnection): String;
- end;}
+ end;
TClipboardHelper = class helper for TClipboard
private
@@ -434,7 +434,7 @@ type
function GetExecutableBits: Byte;
procedure Help(Sender: TObject; Anchor: String);
//function PortOpen(Port: Word): Boolean;
- //function IsValidFilePath(FilePath: String): Boolean;
+ function IsValidFilePath(FilePath: String): Boolean;
//function FileIsWritable(FilePath: String): Boolean;
//function GetProductInfo(dwOSMajorVersion, dwOSMinorVersion, dwSpMajorVersion, dwSpMinorVersion: DWORD; out pdwReturnedProductType: DWORD): BOOL stdcall; external kernel32 delayed;
//function GetCurrentPackageFullName(out Len: Cardinal; Name: PWideChar): Integer; stdcall; external kernel32 delayed;
@@ -2739,7 +2739,7 @@ begin
end;}
-{function IsValidFilePath(FilePath: String): Boolean;
+function IsValidFilePath(FilePath: String): Boolean;
var
Pieces: TStringList;
i: Integer;
@@ -2748,11 +2748,12 @@ begin
Result := True;
Pieces := TStringList.Create;
SplitRegExpr('[\\\/]', FilePath, Pieces);
- for i:=1 to Pieces.Count-1 do begin
+ // Todo: implement cross platformic
+ {for i:=1 to Pieces.Count-1 do begin
Result := Result and TPath.HasValidFileNameChars(Pieces[i], False);
- end;
+ end;}
Pieces.Free;
-end;}
+end;
{function FileIsWritable(FilePath: String): Boolean;
@@ -3398,7 +3399,7 @@ end;
{ TSqlTranspiler }
-{class function TSqlTranspiler.CreateTable(SQL: String; SourceDb, TargetDb: TDBConnection): String;
+class function TSqlTranspiler.CreateTable(SQL: String; SourceDb, TargetDb: TDBConnection): String;
begin
Result := SQL;
@@ -3408,7 +3409,7 @@ begin
Result := ReplaceRegExpr('\sCOLLATE\s\w+(\s+GENERATED\s)', Result, '$1', [rroModifierI, rroUseSubstitution]);
end;
-end;}
+end;
{ TClipboardHelper }
diff --git a/source/main.pas b/source/main.pas
index 4423d446..d2e60c6d 100644
--- a/source/main.pas
+++ b/source/main.pas
@@ -14,7 +14,7 @@ uses
Generics.Defaults, opensslsockets, StdActns, Clipbrd, Types, LCLType, EditBtn,
FileUtil, LMessages, jsonconf, dbconnection, dbstructures, dbstructures.mysql,
generic_types, apphelpers, extra_controls, createdatabase,
- SynEditMarkupSpecialLine, searchreplace, ImgList, IniFiles, LazFileUtils;
+ SynEditMarkupSpecialLine, searchreplace, ImgList, IniFiles, LazFileUtils, tabletools;
type
@@ -1236,7 +1236,7 @@ type
FDBObjectsMaxRows: Int64;
FSearchReplaceDialog: TfrmSearchReplace;
FCreateDatabaseDialog: TCreateDatabaseForm;
- //FTableToolsDialog: TfrmTableTools;
+ FTableToolsDialog: TfrmTableTools;
FGridEditFunctionMode: Boolean;
FClipboardHasNull: Boolean;
FTimeZoneOffset: Integer;
@@ -2967,7 +2967,7 @@ var
DBObj: PDBObject;
begin
// Show table tools dialog
- {FTableToolsDialog := TfrmTableTools.Create(Self);
+ FTableToolsDialog := TfrmTableTools.Create(Self);
FTableToolsDialog.PreSelectObjects.Clear;
if DBTreeClicked(Sender) then
FTableToolsDialog.PreSelectObjects.Add(ActiveDbObj)
@@ -2990,7 +2990,7 @@ begin
else if Sender = actGenerateData then
FTableToolsDialog.ToolMode := tmGenerateData;
FTableToolsDialog.ShowModal;
- FreeAndNil(FTableToolsDialog);}
+ FreeAndNil(FTableToolsDialog);
end;
@@ -13323,8 +13323,8 @@ begin
Editors.Add(SqlHelpDialog.memoDescription);
Editors.Add(SqlHelpDialog.MemoExample);
end;
- {if Assigned(FTableToolsDialog) then
- Editors.Add(FTableToolsDialog.SynMemoFindText);}
+ if Assigned(FTableToolsDialog) then
+ Editors.Add(FTableToolsDialog.SynMemoFindText);
if Assigned(frmCsvDetector) then
Editors.Add(frmCsvDetector.SynMemoCreateTable);
diff --git a/source/tabletools.lfm b/source/tabletools.lfm
new file mode 100644
index 00000000..6618395d
--- /dev/null
+++ b/source/tabletools.lfm
@@ -0,0 +1,846 @@
+object frmTableTools: TfrmTableTools
+ Left = 734
+ Height = 479
+ Top = 126
+ Width = 955
+ BorderIcons = [biSystemMenu, biHelp]
+ Caption = 'Table tools'
+ ClientHeight = 479
+ ClientWidth = 955
+ Color = clBtnFace
+ DesignTimePPI = 120
+ OnClose = FormClose
+ OnCreate = FormCreate
+ OnShow = FormShow
+ Position = poMainFormCenter
+ object lblCheckedSize: TLabel
+ Left = 10
+ Height = 20
+ Top = 441
+ Width = 100
+ Anchors = [akLeft, akBottom]
+ Caption = 'lblCheckedSize'
+ end
+ object btnCloseOrCancel: TButton
+ Left = 826
+ Height = 31
+ Top = 438
+ Width = 119
+ Anchors = [akRight, akBottom]
+ Caption = 'Close'
+ ModalResult = 2
+ TabOrder = 3
+ OnClick = btnCloseOrCancelClick
+ end
+ object pnlTop: TPanel
+ Left = 0
+ Height = 440
+ Top = 0
+ Width = 955
+ Align = alTop
+ Anchors = [akTop, akLeft, akRight, akBottom]
+ BevelOuter = bvNone
+ ClientHeight = 440
+ ClientWidth = 955
+ ParentBackground = False
+ TabOrder = 0
+ object spltHorizontally: TSplitter
+ Cursor = crSizeWE
+ Left = 231
+ Height = 440
+ Top = 0
+ Width = 5
+ OnMoved = spltHorizontallyMoved
+ end
+ object pnlRight: TPanel
+ Left = 236
+ Height = 440
+ Top = 0
+ Width = 719
+ Align = alClient
+ BevelOuter = bvNone
+ ClientHeight = 440
+ ClientWidth = 719
+ ParentBackground = False
+ TabOrder = 0
+ object ResultGrid: TLazVirtualStringTree
+ Left = 0
+ Height = 199
+ Top = 241
+ Width = 719
+ Align = alClient
+ Header.AutoSizeIndex = -1
+ Header.Columns = <>
+ Header.Height = 25
+ Header.MainColumn = -1
+ Header.Options = [hoColumnResize, hoDblClickResize, hoDrag, hoHotTrack, hoShowSortGlyphs, hoVisible, hoDisableAnimatedResize]
+ Header.PopupMenu = MainForm.popupListHeader
+ IncrementalSearch = isAll
+ TabOrder = 0
+ TreeOptions.MiscOptions = [toAcceptOLEDrop, toFullRepaintOnResize, toGridExtensions, toInitOnSave, toToggleOnDblClick, toWheelPanning, toEditOnClick]
+ TreeOptions.PaintOptions = [toHotTrack, toShowButtons, toShowDropmark, toShowHorzGridLines, toShowTreeLines, toShowVertGridLines, toThemeAware, toUseBlendedImages, toUseExplorerTheme]
+ TreeOptions.SelectionOptions = [toExtendedFocus, toFullRowSelect]
+ OnCompareNodes = ResultGridCompareNodes
+ OnGetText = ResultGridGetText
+ OnPaintText = ResultGridPaintText
+ OnGetNodeDataSize = ResultGridGetNodeDataSize
+ OnHeaderClick = ResultGridHeaderClick
+ OnInitNode = ResultGridInitNode
+ end
+ object tabsTools: TPageControl
+ Left = 0
+ Height = 241
+ Top = 0
+ Width = 719
+ ActivePage = tabSQLexport
+ Align = alTop
+ TabIndex = 2
+ TabOrder = 1
+ OnChange = ValidateControls
+ object tabMaintenance: TTabSheet
+ Caption = 'Maintenance'
+ ClientHeight = 208
+ ClientWidth = 711
+ ImageIndex = 39
+ object lblOperation: TLabel
+ Left = 4
+ Height = 20
+ Top = 18
+ Width = 70
+ Caption = 'Operation:'
+ end
+ object lblOptions: TLabel
+ Left = 4
+ Height = 20
+ Top = 49
+ Width = 55
+ Caption = 'Options:'
+ end
+ object comboOperation: TComboBox
+ Left = 100
+ Height = 28
+ Top = 14
+ Width = 584
+ Anchors = [akTop, akLeft, akRight]
+ ItemHeight = 20
+ Style = csDropDownList
+ TabOrder = 0
+ OnChange = ValidateControls
+ end
+ object chkQuick: TCheckBox
+ Left = 101
+ Height = 24
+ Top = 48
+ Width = 58
+ Caption = 'Quick'
+ TabOrder = 1
+ OnClick = ValidateControls
+ end
+ object chkFast: TCheckBox
+ Left = 101
+ Height = 24
+ Top = 71
+ Width = 47
+ Caption = 'Fast'
+ TabOrder = 2
+ OnClick = ValidateControls
+ end
+ object chkMedium: TCheckBox
+ Left = 101
+ Height = 24
+ Top = 95
+ Width = 76
+ Caption = 'Medium'
+ TabOrder = 3
+ OnClick = ValidateControls
+ end
+ object chkExtended: TCheckBox
+ Left = 230
+ Height = 24
+ Top = 48
+ Width = 83
+ Caption = 'Extended'
+ TabOrder = 4
+ OnClick = ValidateControls
+ end
+ object chkChanged: TCheckBox
+ Left = 230
+ Height = 24
+ Top = 71
+ Width = 80
+ Caption = 'Changed'
+ TabOrder = 5
+ OnClick = ValidateControls
+ end
+ object chkUseFrm: TCheckBox
+ Left = 230
+ Height = 24
+ Top = 95
+ Width = 103
+ Caption = 'Use FRM file'
+ TabOrder = 6
+ OnClick = ValidateControls
+ end
+ object btnHelpMaintenance: TButton
+ Left = 591
+ Height = 31
+ Top = 48
+ Width = 94
+ Anchors = [akTop, akRight]
+ Caption = 'Help'
+ TabOrder = 7
+ OnClick = btnHelpMaintenanceClick
+ end
+ object chkForUpgrade: TCheckBox
+ Left = 101
+ Height = 24
+ Top = 120
+ Width = 104
+ Caption = 'For Upgrade'
+ TabOrder = 8
+ OnClick = ValidateControls
+ end
+ end
+ object tabFind: TTabSheet
+ Caption = 'Find text'
+ ClientHeight = 208
+ ClientWidth = 711
+ ImageIndex = 30
+ object lblFindText: TLabel
+ Left = 4
+ Height = 20
+ Top = 34
+ Width = 78
+ Caption = 'Text to find:'
+ end
+ object lblDataTypes: TLabel
+ Left = 4
+ Height = 20
+ Top = 110
+ Width = 155
+ Anchors = [akLeft, akBottom]
+ Caption = 'Search in column types:'
+ end
+ object lblMatchType: TLabel
+ Left = 4
+ Height = 20
+ Top = 172
+ Width = 77
+ Anchors = [akLeft, akBottom]
+ Caption = 'Match type:'
+ end
+ object comboDataTypes: TComboBox
+ Left = 260
+ Height = 28
+ Top = 108
+ Width = 425
+ Anchors = [akLeft, akRight, akBottom]
+ ItemHeight = 20
+ Style = csDropDownList
+ TabOrder = 0
+ end
+ object chkCaseSensitive: TCheckBox
+ Left = 260
+ Height = 24
+ Top = 140
+ Width = 425
+ Anchors = [akLeft, akRight, akBottom]
+ Caption = 'Case sensitive'
+ TabOrder = 1
+ end
+ object comboMatchType: TComboBox
+ Left = 260
+ Height = 28
+ Top = 171
+ Width = 425
+ Anchors = [akLeft, akRight, akBottom]
+ ItemHeight = 20
+ ItemIndex = 0
+ Items.Strings = (
+ 'Left and right wildcard'
+ 'Exact match'
+ 'Left wildcard'
+ 'Right wildcard'
+ 'Regular expression'
+ )
+ Style = csDropDownList
+ TabOrder = 2
+ Text = 'Left and right wildcard'
+ end
+ object tabsTextType: TPageControl
+ Left = 260
+ Height = 97
+ Top = 4
+ Width = 425
+ ActivePage = tabSimpleText
+ Anchors = [akTop, akLeft, akRight, akBottom]
+ TabIndex = 0
+ TabOrder = 3
+ OnChange = ValidateControls
+ object tabSimpleText: TTabSheet
+ Caption = 'Simple text'
+ ClientHeight = 64
+ ClientWidth = 417
+ object memoFindText: TMemo
+ Left = 0
+ Height = 64
+ Top = 0
+ Width = 417
+ Align = alClient
+ ScrollBars = ssVertical
+ TabOrder = 0
+ OnChange = ValidateControls
+ end
+ end
+ object tabSQL: TTabSheet
+ Caption = 'SQL'
+ ClientHeight = 64
+ ClientWidth = 417
+ ImageIndex = 1
+ inline SynMemoFindText: TSynEdit
+ Left = 0
+ Height = 64
+ Top = 0
+ Width = 417
+ Align = alClient
+ Font.Height = -16
+ Font.Name = 'Courier New'
+ Font.Pitch = fpFixed
+ Font.Quality = fqNonAntialiased
+ ParentColor = False
+ ParentFont = False
+ TabOrder = 0
+ Gutter.Width = 72
+ Gutter.MouseActions = <>
+ RightGutter.Width = 0
+ RightGutter.MouseActions = <>
+ Highlighter = MainForm.SynSQLSynUsed
+ Keystrokes = <>
+ MouseActions = <>
+ MouseTextActions = <>
+ MouseSelActions = <>
+ Lines.Strings = (
+ '> NOW()'
+ )
+ Options = [eoAutoIndent, eoGroupUndo, eoShowScrollHint, eoSmartTabs, eoTabsToSpaces, eoDragDropEditing]
+ MouseOptions = [emDragDropEditing]
+ VisibleSpecialChars = [vscSpace, vscTabAtLast]
+ SelectedColor.BackPriority = 50
+ SelectedColor.ForePriority = 50
+ SelectedColor.FramePriority = 50
+ SelectedColor.BoldPriority = 50
+ SelectedColor.ItalicPriority = 50
+ SelectedColor.UnderlinePriority = 50
+ SelectedColor.StrikeOutPriority = 50
+ BracketHighlightStyle = sbhsBoth
+ BracketMatchColor.Background = clNone
+ BracketMatchColor.Foreground = clNone
+ BracketMatchColor.Style = [fsBold]
+ FoldedCodeColor.Background = clNone
+ FoldedCodeColor.Foreground = clGray
+ FoldedCodeColor.FrameColor = clGray
+ MouseLinkColor.Background = clNone
+ MouseLinkColor.Foreground = clBlue
+ LineHighlightColor.Background = clNone
+ LineHighlightColor.Foreground = clNone
+ inline SynLeftGutterPartList1: TSynGutterPartList
+ object SynGutterMarks1: TSynGutterMarks
+ Width = 30
+ MouseActions = <>
+ end
+ object SynGutterLineNumber1: TSynGutterLineNumber
+ Width = 21
+ MouseActions = <>
+ MarkupInfo.Background = clBtnFace
+ MarkupInfo.Foreground = clNone
+ DigitCount = 2
+ ShowOnlyLineNumbersMultiplesOf = 1
+ ZeroStart = False
+ LeadingZeros = False
+ end
+ object SynGutterChanges1: TSynGutterChanges
+ Width = 5
+ MouseActions = <>
+ ModifiedColor = 59900
+ SavedColor = clGreen
+ end
+ object SynGutterSeparator1: TSynGutterSeparator
+ Width = 3
+ MouseActions = <>
+ MarkupInfo.Background = clWhite
+ MarkupInfo.Foreground = clGray
+ end
+ object SynGutterCodeFolding1: TSynGutterCodeFolding
+ Width = 13
+ MouseActions = <>
+ MarkupInfo.Background = clNone
+ MarkupInfo.Foreground = clGray
+ MouseActionsExpanded = <>
+ MouseActionsCollapsed = <>
+ end
+ end
+ end
+ end
+ end
+ end
+ object tabSQLexport: TTabSheet
+ Caption = 'SQL export'
+ ClientHeight = 208
+ ClientWidth = 711
+ ImageIndex = 9
+ object lblExportData: TLabel
+ Left = 4
+ Height = 20
+ Top = 62
+ Width = 35
+ Caption = 'Data:'
+ end
+ object lblExportOutputType: TLabel
+ Left = 4
+ Height = 20
+ Top = 130
+ Width = 49
+ Caption = 'Output:'
+ end
+ object lblExportDatabases: TLabel
+ Left = 4
+ Height = 20
+ Top = 5
+ Width = 82
+ Caption = 'Database(s):'
+ end
+ object lblExportTables: TLabel
+ Left = 4
+ Height = 20
+ Top = 31
+ Width = 54
+ Caption = 'Table(s):'
+ end
+ object lblExportOutputTarget: TLabel
+ Left = 2
+ Height = 20
+ Top = 162
+ Width = 63
+ Caption = 'Filename:'
+ end
+ object lblInsertSize: TLabel
+ Left = 4
+ Height = 20
+ Top = 96
+ Width = 111
+ Caption = 'Max INSERT size:'
+ end
+ object lblInsertSizeUnit: TLabel
+ Left = 302
+ Height = 20
+ Top = 96
+ Width = 155
+ Caption = 'KB (0 = Single INSERTs)'
+ end
+ object btnExportOutputTargetSelect: TSpeedButton
+ Left = 656
+ Height = 26
+ Hint = 'Browse filesystem'
+ Top = 159
+ Width = 29
+ Anchors = [akTop, akRight]
+ ImageIndex = 51
+ OnClick = btnExportOutputTargetSelectClick
+ end
+ object chkExportDatabasesCreate: TCheckBox
+ Left = 240
+ Height = 24
+ Top = 4
+ Width = 64
+ Caption = 'Create'
+ TabOrder = 0
+ OnClick = chkExportOptionClick
+ end
+ object chkExportDatabasesDrop: TCheckBox
+ Left = 125
+ Height = 24
+ Top = 4
+ Width = 55
+ Caption = 'Drop'
+ TabOrder = 1
+ OnClick = chkExportOptionClick
+ end
+ object chkExportTablesDrop: TCheckBox
+ Left = 125
+ Height = 24
+ Top = 30
+ Width = 55
+ Caption = 'Drop'
+ TabOrder = 2
+ OnClick = chkExportOptionClick
+ end
+ object chkExportTablesCreate: TCheckBox
+ Left = 240
+ Height = 24
+ Top = 30
+ Width = 64
+ Caption = 'Create'
+ TabOrder = 3
+ OnClick = chkExportOptionClick
+ end
+ object comboExportData: TComboBox
+ Left = 125
+ Height = 28
+ Top = 59
+ Width = 560
+ Anchors = [akTop, akLeft, akRight]
+ ItemHeight = 20
+ Style = csDropDownList
+ TabOrder = 4
+ OnChange = ValidateControls
+ end
+ object comboExportOutputType: TComboBox
+ Left = 125
+ Height = 28
+ Top = 126
+ Width = 560
+ Anchors = [akTop, akLeft, akRight]
+ DropDownCount = 16
+ ItemHeight = 20
+ Style = csDropDownList
+ TabOrder = 6
+ OnChange = comboExportOutputTypeChange
+ end
+ object comboExportOutputTarget: TComboBox
+ Left = 125
+ Height = 28
+ Top = 159
+ Width = 527
+ Anchors = [akTop, akLeft, akRight]
+ DropDownCount = 16
+ ItemHeight = 20
+ ParentShowHint = False
+ ShowHint = True
+ TabOrder = 8
+ Text = 'comboExportOutputTarget'
+ OnChange = comboExportOutputTargetChange
+ end
+ object editInsertSize: TEdit
+ Left = 125
+ Height = 28
+ Top = 92
+ Width = 150
+ NumbersOnly = True
+ TabOrder = 5
+ Text = '0'
+ end
+ object btnExportOptions: TButton
+ Left = 529
+ Height = 31
+ Top = 90
+ Width = 156
+ Anchors = [akTop, akRight]
+ Caption = 'Options'
+ TabOrder = 7
+ OnClick = btnExportOptionsClick
+ end
+ end
+ object tabBulkTableEdit: TTabSheet
+ Caption = 'Bulk table editor'
+ ClientHeight = 208
+ ClientWidth = 711
+ ImageIndex = 19
+ object chkBulkTableEditDatabase: TCheckBox
+ Left = 4
+ Height = 24
+ Top = 6
+ Width = 144
+ Caption = 'Move to database:'
+ TabOrder = 0
+ OnClick = chkBulkTableEditCheckComboClick
+ end
+ object comboBulkTableEditDatabase: TComboBox
+ Left = 260
+ Height = 28
+ Top = 4
+ Width = 424
+ Anchors = [akTop, akLeft, akRight]
+ Enabled = False
+ ItemHeight = 20
+ Style = csDropDownList
+ TabOrder = 1
+ end
+ object chkBulkTableEditResetAutoinc: TCheckBox
+ Left = 4
+ Height = 24
+ Top = 121
+ Width = 200
+ Caption = 'Reset auto increment value'
+ TabOrder = 2
+ OnClick = ValidateControls
+ end
+ object chkBulkTableEditCollation: TCheckBox
+ Left = 4
+ Height = 24
+ Top = 64
+ Width = 187
+ Caption = 'Change default collation:'
+ TabOrder = 3
+ OnClick = chkBulkTableEditCheckComboClick
+ end
+ object comboBulkTableEditCollation: TComboBox
+ Left = 260
+ Height = 28
+ Top = 61
+ Width = 424
+ Anchors = [akTop, akLeft, akRight]
+ DropDownCount = 16
+ Enabled = False
+ ItemHeight = 20
+ Sorted = True
+ Style = csDropDownList
+ TabOrder = 4
+ end
+ object chkBulkTableEditEngine: TCheckBox
+ Left = 4
+ Height = 24
+ Top = 35
+ Width = 161
+ Caption = 'Change table engine:'
+ TabOrder = 5
+ OnClick = chkBulkTableEditCheckComboClick
+ end
+ object comboBulkTableEditEngine: TComboBox
+ Left = 260
+ Height = 28
+ Top = 32
+ Width = 424
+ Anchors = [akTop, akLeft, akRight]
+ Enabled = False
+ ItemHeight = 20
+ Style = csDropDownList
+ TabOrder = 6
+ end
+ object chkBulkTableEditCharset: TCheckBox
+ Left = 4
+ Height = 24
+ Top = 92
+ Width = 144
+ Caption = 'Convert to charset:'
+ TabOrder = 7
+ OnClick = chkBulkTableEditCheckComboClick
+ end
+ object comboBulkTableEditCharset: TComboBox
+ Left = 260
+ Height = 28
+ Top = 90
+ Width = 424
+ Anchors = [akTop, akLeft, akRight]
+ DropDownCount = 16
+ Enabled = False
+ ItemHeight = 20
+ Style = csDropDownList
+ TabOrder = 8
+ end
+ end
+ object tabGenerateData: TTabSheet
+ Caption = 'Generate data'
+ ClientHeight = 208
+ ClientWidth = 711
+ ImageIndex = 130
+ object lblGenerateDataNumRows: TLabel
+ Left = 4
+ Height = 20
+ Top = 8
+ Width = 110
+ Caption = 'Number of rows:'
+ end
+ object lblGenerateDataNullAmount: TLabel
+ Left = 2
+ Height = 20
+ Top = 42
+ Width = 183
+ Caption = 'Amount of NULLs [percent]:'
+ end
+ object editGenerateDataNumRows: TEdit
+ Left = 250
+ Height = 28
+ Top = 4
+ Width = 151
+ NumbersOnly = True
+ TabOrder = 0
+ Text = '1.000'
+ end
+ object editGenerateDataNullAmount: TEdit
+ Left = 250
+ Height = 28
+ Top = 39
+ Width = 151
+ NumbersOnly = True
+ TabOrder = 1
+ Text = '10'
+ end
+ end
+ end
+ end
+ object pnlLeft: TPanel
+ Left = 0
+ Height = 440
+ Top = 0
+ Width = 231
+ Align = alLeft
+ BevelOuter = bvNone
+ Caption = 'pnlLeft'
+ ClientHeight = 440
+ ClientWidth = 231
+ ParentBackground = False
+ TabOrder = 1
+ object pnlLeftTop: TPanel
+ Left = 0
+ Height = 36
+ Top = 0
+ Width = 231
+ Align = alTop
+ BevelOuter = bvNone
+ Caption = 'pnlLeftTop'
+ ClientHeight = 36
+ ClientWidth = 231
+ ParentBackground = False
+ TabOrder = 0
+ object editDatabaseFilter: TEditButton
+ Left = 5
+ Height = 28
+ Hint = 'Database filter|A list of databases, separated by semicolon. Can contain regular expressions, e.g. "mydb;test.*;project\d+".'
+ Top = 0
+ Width = 61
+ ButtonWidth = 29
+ ImageIndex = 193
+ MaxLength = 0
+ NumGlyphs = 1
+ OnButtonClick = editDatabaseTableFilterRightButtonClick
+ OnChange = editDatabaseTableFilterChange
+ OnKeyPress = editDatabaseTableFilterKeyPress
+ PasswordChar = #0
+ TabOrder = 0
+ Text = 'editDatabaseFilter'
+ TextHint = 'Database filter'
+ end
+ object editTableFilter: TEditButton
+ Left = 74
+ Height = 28
+ Hint = 'Table filter|Can contain regular expressions, e.g. "phpbb_\d"'
+ Top = 0
+ Width = 85
+ ButtonWidth = 29
+ ImageIndex = 193
+ MaxLength = 0
+ NumGlyphs = 1
+ OnButtonClick = editDatabaseTableFilterRightButtonClick
+ OnChange = editDatabaseTableFilterChange
+ OnKeyPress = editDatabaseTableFilterKeyPress
+ PasswordChar = #0
+ TabOrder = 1
+ Text = 'editTableFilter'
+ TextHint = 'Table filter'
+ end
+ end
+ object TreeObjects: TLazVirtualStringTree
+ Left = 0
+ Height = 404
+ Top = 36
+ Width = 231
+ Align = alClient
+ Header.AutoSizeIndex = 0
+ Header.Columns = <
+ item
+ Position = 0
+ Text = 'Dummy, keeps compatibility to mainform.dbtree'
+ Width = 164
+ end
+ item
+ Alignment = taRightJustify
+ Position = 1
+ Text = 'Size'
+ end>
+ Header.Height = 32
+ Header.Options = [hoAutoResize, hoColumnResize, hoDrag, hoShowSortGlyphs]
+ IncrementalSearch = isInitializedOnly
+ PopupMenu = popupTree
+ TabOrder = 1
+ TreeOptions.MiscOptions = [toAcceptOLEDrop, toCheckSupport, toFullRepaintOnResize, toInitOnSave, toToggleOnDblClick, toWheelPanning, toEditOnClick]
+ TreeOptions.PaintOptions = [toHotTrack, toShowButtons, toShowDropmark, toShowRoot, toShowTreeLines, toThemeAware, toUseBlendedImages, toGhostedIfUnfocused, toUseExplorerTheme, toHideTreeLinesIfThemed]
+ TreeOptions.SelectionOptions = [toFullRowSelect, toRightClickSelect]
+ OnBeforeCellPaint = TreeObjectsBeforeCellPaint
+ OnChange = TreeObjectsChange
+ OnChecked = TreeObjectsChecked
+ OnChecking = TreeObjectsChecking
+ OnExpanded = TreeObjectsExpanded
+ OnGetText = TreeObjectsGetText
+ OnPaintText = TreeObjectsPaintText
+ OnGetImageIndex = TreeObjectsGetImageIndex
+ OnGetNodeDataSize = TreeObjectsGetNodeDataSize
+ OnInitChildren = TreeObjectsInitChildren
+ OnInitNode = TreeObjectsInitNode
+ end
+ end
+ end
+ object btnExecute: TButton
+ Left = 700
+ Height = 31
+ Top = 438
+ Width = 119
+ Anchors = [akRight, akBottom]
+ Caption = 'Execute'
+ TabOrder = 2
+ OnClick = Execute
+ end
+ object btnSeeResults: TButton
+ Left = 545
+ Height = 31
+ Top = 438
+ Width = 148
+ Anchors = [akRight, akBottom]
+ Caption = 'See results'
+ ModalResult = 1
+ TabOrder = 1
+ Visible = False
+ OnClick = btnSeeResultsClick
+ end
+ object popupTree: TPopupMenu
+ Left = 180
+ Top = 440
+ object menuCheckNone: TMenuItem
+ Caption = 'Check none'
+ OnClick = CheckAllClick
+ end
+ object menuCheckAll: TMenuItem
+ Caption = 'Check all'
+ OnClick = CheckAllClick
+ end
+ object menuCheckByType: TMenuItem
+ Caption = 'Check ...'
+ end
+ end
+ object popupExportOptions: TPopupMenu
+ Left = 220
+ Top = 440
+ object menuExportAddComments: TMenuItem
+ AutoCheck = True
+ Caption = 'Add comments'
+ end
+ object menuExportRemoveAutoIncrement: TMenuItem
+ AutoCheck = True
+ Caption = 'Remove AUTO_INCREMENT clauses'
+ end
+ object menuExportRemoveDefiner: TMenuItem
+ AutoCheck = True
+ Caption = 'Remove DEFINER clauses'
+ end
+ object menuCopyMysqldumpCommand: TMenuItem
+ Caption = 'Copy mysqldump command'
+ OnClick = menuCopyMysqldumpCommandClick
+ end
+ end
+ object timerCalcSize: TTimer
+ Enabled = False
+ Interval = 200
+ OnTimer = timerCalcSizeTimer
+ Left = 330
+ Top = 440
+ end
+end
diff --git a/source/tabletools.pas b/source/tabletools.pas
new file mode 100644
index 00000000..301b13c6
--- /dev/null
+++ b/source/tabletools.pas
@@ -0,0 +1,2439 @@
+unit tabletools;
+
+{$mode delphi}{$H+}
+
+// -------------------------------------
+// Table-diagnostics
+// -------------------------------------
+
+
+interface
+
+uses
+ SysUtils, Classes, Controls, Forms, StdCtrls, ComCtrls, Buttons, Dialogs,
+ laz.VirtualTrees, ExtCtrls, Graphics, RegExpr, Math, Generics.Collections, extra_controls,
+ dbconnection, apphelpers, Menus, DateUtils, Zipper, ZStream, StrUtils,
+ SynEdit, ClipBrd, generic_types, fpjson, Variants, EditBtn, LazFileUtils;
+
+type
+ TToolMode = (tmMaintenance, tmFind, tmSQLExport, tmBulkTableEdit, tmGenerateData);
+ TfrmTableTools = class(TExtForm)
+ btnCloseOrCancel: TButton;
+ pnlTop: TPanel;
+ spltHorizontally: TSplitter;
+ pnlRight: TPanel;
+ ResultGrid: TLazVirtualStringTree;
+ tabsTools: TPageControl;
+ tabMaintenance: TTabSheet;
+ comboOperation: TComboBox;
+ lblOperation: TLabel;
+ chkQuick: TCheckBox;
+ chkFast: TCheckBox;
+ chkMedium: TCheckBox;
+ chkExtended: TCheckBox;
+ chkChanged: TCheckBox;
+ chkUseFrm: TCheckBox;
+ lblOptions: TLabel;
+ btnHelpMaintenance: TButton;
+ tabFind: TTabSheet;
+ lblFindText: TLabel;
+ comboDataTypes: TComboBox;
+ lblDataTypes: TLabel;
+ tabSQLexport: TTabSheet;
+ chkExportDatabasesCreate: TCheckBox;
+ chkExportDatabasesDrop: TCheckBox;
+ chkExportTablesDrop: TCheckBox;
+ chkExportTablesCreate: TCheckBox;
+ lblExportData: TLabel;
+ comboExportData: TComboBox;
+ lblExportOutputType: TLabel;
+ comboExportOutputType: TComboBox;
+ comboExportOutputTarget: TComboBox;
+ lblExportDatabases: TLabel;
+ lblExportTables: TLabel;
+ lblExportOutputTarget: TLabel;
+ btnExecute: TButton;
+ btnExportOutputTargetSelect: TSpeedButton;
+ tabBulkTableEdit: TTabSheet;
+ chkBulkTableEditDatabase: TCheckBox;
+ comboBulkTableEditDatabase: TComboBox;
+ chkBulkTableEditResetAutoinc: TCheckBox;
+ chkBulkTableEditCollation: TCheckBox;
+ comboBulkTableEditCollation: TComboBox;
+ chkBulkTableEditEngine: TCheckBox;
+ comboBulkTableEditEngine: TComboBox;
+ chkBulkTableEditCharset: TCheckBox;
+ comboBulkTableEditCharset: TComboBox;
+ btnSeeResults: TButton;
+ chkCaseSensitive: TCheckBox;
+ lblCheckedSize: TLabel;
+ popupTree: TPopupMenu;
+ menuCheckAll: TMenuItem;
+ menuCheckByType: TMenuItem;
+ menuCheckNone: TMenuItem;
+ chkForUpgrade: TCheckBox;
+ lblInsertSize: TLabel;
+ editInsertSize: TEdit;
+ lblInsertSizeUnit: TLabel;
+ btnExportOptions: TButton;
+ popupExportOptions: TPopupMenu;
+ menuExportAddComments: TMenuItem;
+ menuExportRemoveAutoIncrement: TMenuItem;
+ comboMatchType: TComboBox;
+ lblMatchType: TLabel;
+ menuExportRemoveDefiner: TMenuItem;
+ tabsTextType: TPageControl;
+ tabSimpleText: TTabSheet;
+ tabSQL: TTabSheet;
+ memoFindText: TMemo;
+ SynMemoFindText: TSynEdit;
+ menuCopyMysqldumpCommand: TMenuItem;
+ pnlLeft: TPanel;
+ pnlLeftTop: TPanel;
+ editDatabaseFilter: TEditButton;
+ editTableFilter: TEditButton;
+ TreeObjects: TLazVirtualStringTree;
+ timerCalcSize: TTimer;
+ tabGenerateData: TTabSheet;
+ lblGenerateDataNumRows: TLabel;
+ editGenerateDataNumRows: TEdit;
+ lblGenerateDataNullAmount: TLabel;
+ editGenerateDataNullAmount: TEdit;
+ procedure FormCreate(Sender: TObject);
+ procedure FormShow(Sender: TObject);
+ procedure btnHelpMaintenanceClick(Sender: TObject);
+ function GetCheckedObjects(DBNode: PVirtualNode): TDBObjectList;
+ procedure TreeObjectsGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
+ TextType: TVSTTextType; var CellText: String);
+ procedure TreeObjectsInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode;
+ var InitialStates: TVirtualNodeInitStates);
+ procedure TreeObjectsGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind;
+ Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: TImageIndex);
+ procedure TreeObjectsInitChildren(Sender: TBaseVirtualTree; Node: PVirtualNode; var ChildCount: Cardinal);
+ procedure Execute(Sender: TObject);
+ procedure ResultGridInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode;
+ var InitialStates: TVirtualNodeInitStates);
+ procedure ResultGridGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer);
+ procedure ResultGridGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
+ TextType: TVSTTextType; var CellText: String);
+ procedure TreeObjectsChecked(Sender: TBaseVirtualTree; Node: PVirtualNode);
+ procedure FillTargetDatabases;
+ procedure ResultGridHeaderClick(Sender: TVTHeader; HitInfo: TVTHeaderHitInfo);
+ procedure ResultGridCompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode;
+ Column: TColumnIndex; var Result: Integer);
+ procedure ResultGridPaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; Node: PVirtualNode;
+ Column: TColumnIndex; TextType: TVSTTextType);
+ procedure ValidateControls(Sender: TObject);
+ procedure SaveSettings(Sender: TObject);
+ procedure chkExportOptionClick(Sender: TObject);
+ procedure btnExportOutputTargetSelectClick(Sender: TObject);
+ procedure comboExportOutputTargetChange(Sender: TObject);
+ procedure comboExportOutputTypeChange(Sender: TObject);
+ procedure FormClose(Sender: TObject; var Action: TCloseAction);
+ procedure TreeObjectsPaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; Node: PVirtualNode;
+ Column: TColumnIndex; TextType: TVSTTextType);
+ procedure chkBulkTableEditCheckComboClick(Sender: TObject);
+ procedure TreeObjectsChange(Sender: TBaseVirtualTree; Node: PVirtualNode);
+ procedure TreeObjectsChecking(Sender: TBaseVirtualTree; Node: PVirtualNode; var NewState: TCheckState;
+ var Allowed: Boolean);
+ procedure btnSeeResultsClick(Sender: TObject);
+ procedure TreeObjectsGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer);
+ procedure btnCloseOrCancelClick(Sender: TObject);
+ procedure TreeObjectsBeforeCellPaint(Sender: TBaseVirtualTree; TargetCanvas: TCanvas;
+ Node: PVirtualNode; Column: TColumnIndex; CellPaintMode: TVTCellPaintMode; CellRect: TRect;
+ var ContentRect: TRect);
+ procedure CheckAllClick(Sender: TObject);
+ procedure TreeObjectsExpanded(Sender: TBaseVirtualTree; Node: PVirtualNode);
+ procedure btnExportOptionsClick(Sender: TObject);
+ procedure menuCopyMysqldumpCommandClick(Sender: TObject);
+ procedure spltHorizontallyMoved(Sender: TObject);
+ procedure editDatabaseTableFilterChange(Sender: TObject);
+ procedure editDatabaseTableFilterKeyPress(Sender: TObject; var Key: Char);
+ procedure editDatabaseTableFilterRightButtonClick(Sender: TObject);
+ procedure timerCalcSizeTimer(Sender: TObject);
+ const
+ StatusMsg = '%s %s ...';
+ private
+ { Private declarations }
+ FResults: TObjectList;
+ FToolMode: TToolMode;
+ FSecondExportPass: Boolean; // Set to True after everything is exported and final VIEWs need to be exported again
+ FCancelled: Boolean;
+ ExportStream: TStream;
+ FExportFileName: String;
+ ExportStreamStartOfQueryPos: Int64;
+ ExportLastDatabase: String;
+ FTargetConnection: TDBConnection;
+ FLastOutputSelectedIndex: Integer;
+ FModifiedDbs: TStringList;
+ FHeaderCreated: Boolean;
+ FFindSeeResultSQL: TStringList;
+ ToFile, ToDir, ToClipboard, ToDb, ToServer: Boolean;
+ FObjectSizes, FObjectSizesDone, FObjectSizesDoneExact: Int64;
+ FStartTimeAll: Cardinal;
+ //procedure WMNCLBUTTONDOWN(var Msg: TWMNCLButtonDown) ; message WM_NCLBUTTONDOWN;
+ //procedure WMNCLBUTTONUP(var Msg: TWMNCLButtonUp) ; message WM_NCLBUTTONUP;
+ procedure SetToolMode(Value: TToolMode);
+ procedure Output(SQL: String; IsEndOfQuery, ForFile, ForDir, ForDb, ForServer: Boolean);
+ procedure AddResults(SQL: String; Connection: TDBConnection);
+ procedure AddNotes(Col1, Col2, Col3, Col4: String); overload;
+ procedure AddNotes(DBObject: TDBObject; Msg1, Msg2: String); overload;
+ procedure SetupResultGrid(Results: TDBQuery=nil);
+ procedure UpdateResultGrid;
+ procedure DoMaintenance(DBObj: TDBObject);
+ procedure DoFind(DBObj: TDBObject);
+ procedure DoExport(DBObj: TDBObject);
+ procedure DoBulkTableEdit(DBObj: TDBObject);
+ procedure DoBeforeGenerateData(Sender: TObject);
+ procedure DoGenerateData(DBObj: TDBObject);
+ procedure DoAfterGenerateData(Sender: TObject);
+ public
+ { Public declarations }
+ PreSelectObjects: TDBObjectList;
+ property ToolMode: TToolMode read FToolMode write SetToolMode;
+ end;
+
+
+implementation
+
+uses main, dbstructures;
+
+const
+ STRSKIPPED: String = 'Skipped - ';
+ EXPORT_FILE_FOOTER =
+ '/*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, ''system'') */;'+CRLF+
+ '/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '''') */;'+CRLF+
+ '/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */;'+CRLF+
+ '/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;'+CRLF+
+ '/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */;'+CRLF;
+
+var
+ OUTPUT_FILE,
+ OUTPUT_FILE_COMPRESSED,
+ OUTPUT_CLIPBOARD,
+ OUTPUT_DIR,
+ OUTPUT_DB,
+ OUTPUT_SERVER,
+ DATA_NO,
+ DATA_REPLACE,
+ DATA_INSERT,
+ DATA_INSERTNEW,
+ DATA_UPDATE : String;
+
+{$R *.LFM}
+
+
+{procedure TfrmTableTools.WMNCLBUTTONDOWN(var Msg: TWMNCLButtonDown) ;
+begin
+ if Msg.HitTest = HTHELP then
+ Msg.Result := 0 // "eat" the message
+ else
+ inherited;
+end;}
+
+
+{procedure TfrmTableTools.WMNCLBUTTONUP(var Msg: TWMNCLButtonUp) ;
+begin
+ if Msg.HitTest = HTHELP then begin
+ Msg.Result := 0;
+ if tabsTools.ActivePage = tabSQLexport then
+ Help(Self, 'sqlexport')
+ else
+ ErrorDialog(_('No help available for this tab.'));
+ end else
+ inherited;
+end;}
+
+
+procedure TfrmTableTools.FormCreate(Sender: TObject);
+var
+ i: Integer;
+ dtc: TDBDatatypeCategoryIndex;
+ SessionPaths: TStringList;
+ MenuItem: TMenuItem;
+ dt: TListNodeType;
+ Obj: TDBObject;
+begin
+ HasSizeGrip := True;
+ OUTPUT_FILE := _('Single .sql file');
+ OUTPUT_FILE_COMPRESSED := _('ZIP compressed .sql file');
+ OUTPUT_CLIPBOARD := _('Clipboard');
+ OUTPUT_DIR := _('Directory - one file per object in database subdirectories');
+ OUTPUT_DB := _('Database');
+ OUTPUT_SERVER := _('Server')+': ';
+ // Todo: sanitize misleading names
+ DATA_NO := _('No data');
+ DATA_REPLACE := _('Delete + insert (truncate existing data)');
+ DATA_INSERT := _('Insert');
+ DATA_INSERTNEW := _('Insert ignore (do not update existing)');
+ DATA_UPDATE := _('Replace existing data');
+
+ // Find text tab
+ memoFindText.Text := AppSettings.ReadString(asTableToolsFindText);
+ SynMemoFindText.Text := AppSettings.ReadString(asTableToolsFindSQL);
+ tabsTextType.ActivePageIndex := AppSettings.ReadInt(asTableToolsFindTextTab);
+ comboDatatypes.Items.Add(_('All data types'));
+ for dtc:=Low(DatatypeCategories) to High(DatatypeCategories) do
+ comboDatatypes.Items.Add(DatatypeCategories[dtc].Name);
+ comboDatatypes.ItemIndex := AppSettings.ReadInt(asTableToolsDatatype);
+ chkCaseSensitive.Checked := AppSettings.ReadBool(asTableToolsFindCaseSensitive);
+ comboMatchType.ItemIndex := AppSettings.ReadInt(asTableToolsFindMatchType);
+
+ // SQL export tab
+ chkExportDatabasesCreate.Checked := AppSettings.ReadBool(asExportSQLCreateDatabases);
+ chkExportTablesCreate.Checked := AppSettings.ReadBool(asExportSQLCreateTables);
+ comboExportData.Items.Text := DATA_NO+CRLF +DATA_REPLACE+CRLF +DATA_INSERT+CRLF +DATA_INSERTNEW+CRLF +DATA_UPDATE;
+ comboExportData.ItemIndex := AppSettings.ReadInt(asExportSQLDataHow);
+ editInsertSize.Text := AppSettings.ReadInt(asExportSQLDataInsertSize).ToString;
+ menuExportAddComments.Checked := AppSettings.ReadBool(asExportSQLAddComments);
+ menuExportRemoveAutoIncrement.Checked := AppSettings.ReadBool(asExportSQLRemoveAutoIncrement);
+ menuExportRemoveDefiner.Checked := AppSettings.ReadBool(asExportSQLRemoveDefiner);
+ // Add hardcoded output options and session names from registry
+ comboExportOutputType.Items.Text :=
+ OUTPUT_FILE + CRLF +
+ OUTPUT_FILE_COMPRESSED + CRLF +
+ OUTPUT_DIR + CRLF +
+ OUTPUT_CLIPBOARD + CRLF +
+ OUTPUT_DB;
+ SessionPaths := TStringList.Create;
+ AppSettings.GetSessionPaths('', SessionPaths);
+ for i:=0 to SessionPaths.Count-1 do begin
+ if SessionPaths[i] <> Mainform.ActiveConnection.Parameters.SessionPath then
+ comboExportOutputType.Items.Add(OUTPUT_SERVER+SessionPaths[i]);
+ end;
+ SessionPaths.Free;
+ comboExportOutputTarget.Text := '';
+
+ // Generate data tab
+ editGenerateDataNumRows.Text := AppSettings.ReadInt(asGenerateDataNumRows).ToString;
+ editGenerateDataNullAmount.Text := AppSettings.ReadInt(asGenerateDataNullAmount).ToString;
+
+ // Various
+ FixVT(TreeObjects);
+ FixVT(ResultGrid);
+ FResults := TObjectList.Create;
+ PreSelectObjects := TDBObjectList.Create(False);
+ FModifiedDbs := TStringList.Create;
+ FFindSeeResultSQL := TStringList.Create;
+
+ // Popup menu
+ Obj := TDBObject.Create(nil);
+ for dt:=lntTable to lntEvent do begin
+ Obj.NodeType := dt;
+ MenuItem := TMenuItem.Create(menuCheckByType);
+ MenuItem.Caption := _(Obj.ObjType+'s');
+ MenuItem.ImageIndex := Obj.ImageIndex;
+ MenuItem.OnClick := CheckAllClick;
+ MenuItem.Tag := Integer(dt);
+ menuCheckByType.Add(MenuItem);
+ end;
+ Obj.Free;
+
+ // Restore GUI setup
+ Width := AppSettings.ReadInt(asTableToolsWindowWidth);
+ Height := AppSettings.ReadInt(asTableToolsWindowHeight);
+ pnlLeft.Width := AppSettings.ReadInt(asTableToolsTreeWidth);
+end;
+
+
+procedure TfrmTableTools.FormShow(Sender: TObject);
+var
+ Node, FirstChecked: PVirtualNode;
+ idx: Integer;
+ DBObj: TDBObject;
+begin
+ // When this form is displayed the second time, databases may be deleted or filtered.
+ // Also, checked nodes must be unchecked and unchecked nodes may need to be checked.
+ TreeObjects.Clear;
+ TreeObjects.RootNodeCount := Mainform.DBtree.RootNodeCount;
+
+ FObjectSizes := 0;
+
+ // Init all objects in active database, so the tree does not just check the db node
+ // if we want the first child only. See issue #2267.
+ Node := MainForm.FindDBNode(TreeObjects, MainForm.ActiveConnection, MainForm.ActiveDatabase);
+ Node := TreeObjects.GetFirstChild(Node);
+ while Assigned(Node) do
+ Node := TreeObjects.GetNextSibling(Node);
+ for DBObj in PreSelectObjects do begin
+ Node := MainForm.FindDBObjectNode(TreeObjects, DBObj);
+ if Assigned(Node) then
+ TreeObjects.CheckState[Node] := csCheckedNormal;
+ end;
+
+ FirstChecked := TreeObjects.GetFirstChecked;
+ if Assigned(FirstChecked) then
+ SelectNode(TreeObjects, FirstChecked);
+ // CHECKSUM available since MySQL 4.1.1
+ idx := comboOperation.ItemIndex;
+ if idx = -1 then idx := 0;
+ comboOperation.Items.CommaText := 'Check,Analyze,Checksum,Optimize,Repair';
+ if Mainform.ActiveConnection.ServerVersionInt < 40101 then
+ comboOperation.Items.Text := StringReplace(comboOperation.Items.Text, 'Checksum', 'Checksum ('+_(SUnsupported)+')', [rfReplaceAll]);
+ comboOperation.ItemIndex := idx;
+ comboOperation.OnChange(Sender);
+
+ // Restore output option. Avoid server preselection to avoid unwanted connects.
+ // See issue #3411
+ idx := AppSettings.ReadInt(asExportSQLOutput);
+ if (idx = -1)
+ or (idx >= comboExportOutputType.Items.Count)
+ or StartsStr(OUTPUT_SERVER, comboExportOutputType.Items[idx])
+ then idx := 0;
+ comboExportOutputType.ItemIndex := idx;
+ comboExportOutputType.OnChange(Sender);
+
+ comboBulkTableEditDatabase.Items.Text := Mainform.ActiveConnection.AllDatabases.Text;
+ if comboBulkTableEditDatabase.Items.Count > 0 then
+ comboBulkTableEditDatabase.ItemIndex := 0;
+
+ comboBulkTableEditEngine.Items := MainForm.ActiveConnection.TableEngines;
+ if comboBulkTableEditEngine.Items.Count > 0 then
+ comboBulkTableEditEngine.ItemIndex := comboBulkTableEditEngine.Items.IndexOf(MainForm.ActiveConnection.TableEngineDefault);
+
+ comboBulkTableEditCollation.Items := MainForm.ActiveConnection.CollationList;
+ if comboBulkTableEditCollation.Items.Count > 0 then
+ comboBulkTableEditCollation.ItemIndex := 0;
+
+ comboBulkTableEditCharset.Items := MainForm.ActiveConnection.CharsetList;
+ if comboBulkTableEditCharset.Items.Count > 0 then
+ comboBulkTableEditCharset.ItemIndex := 0;
+
+ MainForm.SetupSynEditors(Self);
+ MainForm.SynCompletionProposal.AddEditor(SynMemoFindText);
+
+ pnlLeftTop.Height := editDatabaseFilter.Height + 2;
+ // Fixes width of filter edits:
+ spltHorizontallyMoved(Self);
+ // Apply filters:
+ editDatabaseFilter.Text := MainForm.editDatabaseFilter.Text;
+ editTableFilter.Text := MainForm.editTableFilter.Text;
+
+ ValidateControls(Sender);
+end;
+
+
+procedure TfrmTableTools.menuCopyMysqldumpCommandClick(Sender: TObject);
+var
+ BinPath, ConnectionArguments, FullCommand: String;
+ Arguments, DatabaseNames: TStringList;
+ Conn: TDBConnection;
+ SessionNode, DBNode: PVirtualNode;
+ DBObj: PDBObject;
+begin
+ // Copy command line for use with mysqldump
+ Screen.Cursor := crHourGlass;
+ Conn := MainForm.ActiveConnection;
+
+ BinPath := AppSettings.ReadString(asMySQLBinaries);
+ if (not BinPath.IsEmpty) and (BinPath[Length(BinPath)] <> DirSep) then
+ BinPath := BinPath + DirSep;
+ BinPath := BinPath + IfThen(IsWine, 'mysqldump', 'mysqldump.exe');
+
+ ConnectionArguments := Conn.Parameters.GetExternalCliArguments(nil, nbUnset);
+
+ Arguments := TStringList.Create;
+
+ if chkExportDatabasesDrop.Checked then
+ Arguments.Add('--add-drop-database');
+ if not chkExportDatabasesCreate.Checked then
+ Arguments.Add('--no-create-db');
+ if chkExportTablesDrop.Checked then
+ Arguments.Add('--add-drop-table');
+ if not chkExportTablesCreate.Checked then
+ Arguments.Add('--no-create-info');
+ // Data output. No support for delete+insert - will just use inserts.
+ if comboExportData.Text = DATA_NO then
+ Arguments.Add('--no-data')
+ else if comboExportData.Text = DATA_UPDATE then
+ Arguments.Add('--replace')
+ else if comboExportData.Text = DATA_INSERTNEW then
+ Arguments.Add('--insert-ignore');
+ // Output file. No support for server, database and clipboard
+ if comboExportOutputType.Text = OUTPUT_FILE then
+ Arguments.Add('--result-file="'+comboExportOutputTarget.Text+'"')
+ else if comboExportOutputType.Text = OUTPUT_DIR then
+ Arguments.Add('--tab="'+comboExportOutputTarget.Text+'"');
+
+ // Use checked database names
+ DatabaseNames := TStringList.Create;
+ SessionNode := TreeObjects.GetFirstChild(nil);
+ while Assigned(SessionNode) do begin
+ DBNode := TreeObjects.GetFirstChild(SessionNode);
+ while Assigned(DBNode) do begin
+ if not (DBNode.CheckState in [csUncheckedNormal, csUncheckedPressed]) then begin
+ DBObj := TreeObjects.GetNodeData(DBNode);
+ DatabaseNames.Add(DBObj.Database);
+ // Todo: loop through tables?
+ // CheckedObjects := GetCheckedObjects(DBNode);
+ end;
+ DBNode := TreeObjects.GetNextSibling(DBNode);
+ end;
+ SessionNode := TreeObjects.GetNextSibling(SessionNode);
+ end;
+ // --databases or --all-databases can't be used with --tab
+ if comboExportOutputType.Text <> OUTPUT_DIR then
+ Arguments.Add('--databases ' + Implode(' ', DatabaseNames))
+ else
+ Arguments.Add(Implode(' ', DatabaseNames));
+ DatabaseNames.Free;
+
+ FullCommand := BinPath + ConnectionArguments + ' ' + Implode(' ', Arguments);
+ Arguments.Free;
+
+ Clipboard.TryAsText := FullCommand;
+ Screen.Cursor := crDefault;
+end;
+
+
+procedure TfrmTableTools.FormClose(Sender: TObject; var Action: TCloseAction);
+begin
+ // Auto close temorary connection
+ if Assigned(FTargetConnection) then
+ FreeAndNil(FTargetConnection);
+ // Save GUI setup
+ AppSettings.WriteInt(asTableToolsWindowWidth, Width);
+ AppSettings.WriteInt(asTableToolsWindowHeight, Height);
+ AppSettings.WriteInt(asTableToolsTreeWidth, pnlLeft.Width);
+end;
+
+
+procedure TfrmTableTools.SaveSettings(Sender: TObject);
+var
+ i: Integer;
+ Items: TStringList;
+begin
+ case ToolMode of
+ tmFind: begin
+ AppSettings.WriteInt(asTableToolsFindTextTab, tabsTextType.ActivePageIndex);
+ AppSettings.WriteString(asTableToolsFindText, memoFindText.Text);
+ AppSettings.WriteString(asTableToolsFindSQL, SynMemoFindText.Text);
+ AppSettings.WriteInt(asTableToolsDatatype, comboDatatypes.ItemIndex);
+ AppSettings.WriteBool(asTableToolsFindCaseSensitive, chkCaseSensitive.Checked);
+ AppSettings.WriteInt(asTableToolsFindMatchType, comboMatchType.ItemIndex);
+ end;
+
+ tmSQLExport: begin
+ AppSettings.WriteBool(asExportSQLCreateDatabases, chkExportDatabasesCreate.Checked);
+ AppSettings.WriteBool(asExportSQLCreateTables, chkExportTablesCreate.Checked);
+ AppSettings.WriteInt(asExportSQLDataHow, comboExportData.ItemIndex);
+ if comboExportData.ItemIndex > 0 then
+ AppSettings.WriteInt(asExportSQLDataInsertSize, StrToInt64Def(editInsertSize.Text, 0));
+ AppSettings.WriteBool(asExportSQLAddComments, menuExportAddComments.Checked);
+ AppSettings.WriteBool(asExportSQLRemoveAutoIncrement, menuExportRemoveAutoIncrement.Checked);
+ AppSettings.WriteBool(asExportSQLRemoveDefiner, menuExportRemoveDefiner.Checked);
+
+ if not StartsStr(OUTPUT_SERVER, comboExportOutputType.Text) then
+ AppSettings.WriteInt(asExportSQLOutput, comboExportOutputType.ItemIndex);
+
+ // Remove duplicates from recent file pulldown
+ if (comboExportOutputType.Text = OUTPUT_FILE)
+ or (comboExportOutputType.Text = OUTPUT_FILE_COMPRESSED)
+ or (comboExportOutputType.Text = OUTPUT_DIR)
+ then begin
+ Items := TStringList.Create;
+ Items.Assign(comboExportOutputTarget.Items);
+ Items.Insert(0, comboExportOutputTarget.Text);
+ for i:=Items.Count-1 downto 1 do begin
+ if Items[i] = comboExportOutputTarget.Text then
+ Items.Delete(i);
+ end;
+ comboExportOutputTarget.Items.Assign(Items);
+ Items.Free;
+ end;
+
+ if comboExportOutputType.Text = OUTPUT_FILE then begin
+ AppSettings.WriteString(asExportSQLFilenames, comboExportOutputTarget.Items.Text);
+ end else if comboExportOutputType.Text = OUTPUT_FILE_COMPRESSED then begin
+ AppSettings.WriteString(asExportZIPFilenames, comboExportOutputTarget.Items.Text);
+ end else if comboExportOutputType.Text = OUTPUT_DIR then begin
+ AppSettings.WriteString(asExportSQLDirectories, comboExportOutputTarget.Items.Text);
+ end else if comboExportOutputType.Text = OUTPUT_DB then begin
+ AppSettings.WriteString(asExportSQLDatabase, comboExportOutputTarget.Text);
+ end else if copy(comboExportOutputType.Text, 1, Length(OUTPUT_SERVER)) = OUTPUT_SERVER then begin
+ AppSettings.WriteString(asExportSQLServerDatabase, comboExportOutputTarget.Text);
+ end;
+ end;
+
+ tmGenerateData: begin
+ AppSettings.WriteInt(asGenerateDataNumRows, StrToInt64Def(editGenerateDataNumRows.Text, 0));
+ AppSettings.WriteInt(asGenerateDataNullAmount, StrToInt64Def(editGenerateDataNullAmount.Text, 0));
+ end;
+
+ end;
+
+end;
+
+
+procedure TfrmTableTools.ValidateControls(Sender: TObject);
+var
+ SomeChecked, OptionChecked, FindModeSQL: Boolean;
+ op: String;
+ i: Integer;
+begin
+ // Fired after various user clicks, and also on implicit child node checking
+ SomeChecked := TreeObjects.CheckedCount > 0;
+ TExtForm.PageControlTabHighlight(tabsTools);
+ btnSeeResults.Visible := tabsTools.ActivePage = tabFind;
+ lblCheckedSize.Caption := f_('Selected objects size: %s', [FormatByteNumber(FObjectSizes)]);
+ if tabsTools.ActivePage = tabMaintenance then begin
+ btnExecute.Caption := _('Execute');
+ btnExecute.Enabled := (Pos(_(SUnsupported), comboOperation.Text) = 0) and SomeChecked;
+ // Only enable available options
+ op := LowerCase(comboOperation.Text);
+ chkQuick.Enabled := (op = 'check') or (op = 'checksum') or (op = 'repair');
+ chkFast.Enabled := op = 'check';
+ chkMedium.Enabled := op = 'check';
+ chkExtended.Enabled := (op = 'check') or (op = 'checksum') or (op = 'repair');
+ chkChanged.Enabled := op = 'check';
+ chkUseFrm.Enabled := op = 'repair';
+ chkForUpgrade.Enabled := op = 'check';
+ // CHECKSUM's options are mutually exclusive
+ if comboOperation.Text = 'Checksum' then begin
+ if (Sender = chkExtended) and chkExtended.Checked then chkQuick.Checked := False
+ else if chkQuick.Checked then chkExtended.Checked := False;
+ end;
+ end else if tabsTools.ActivePage = tabFind then begin
+ btnExecute.Caption := _('Find');
+ btnExecute.Enabled := SomeChecked;
+ FindModeSQL := tabsTextType.ActivePage = tabSQL;
+ chkCaseSensitive.Enabled := not FindModeSQL;
+ lblMatchType.Enabled := not FindModeSQL;
+ comboMatchType.Enabled := not FindModeSQL;
+ // Enable "See results" button if there were results
+ btnSeeResults.Enabled := False;
+ if Assigned(FResults) then for i:=0 to FResults.Count-1 do begin
+ if MakeInt(FResults[i][2]) > 0 then begin
+ btnSeeResults.Enabled := True;
+ break;
+ end;
+ end;
+ end else if tabsTools.ActivePage = tabSQLExport then begin
+ btnExecute.Caption := _('Export');
+ btnExecute.Enabled := SomeChecked and ((comboExportOutputTarget.Text <> '') or (not comboExportOutputTarget.Enabled));
+ lblInsertSize.Enabled := comboExportData.ItemIndex > 0;
+ editInsertSize.Enabled := lblInsertSize.Enabled;
+ lblInsertSizeUnit.Enabled := lblInsertSize.Enabled;
+ end else if tabsTools.ActivePage = tabBulkTableEdit then begin
+ btnExecute.Caption := _('Update');
+ chkBulkTableEditCollation.Enabled := MainForm.ActiveConnection.IsUnicode;
+ chkBulkTableEditCharset.Enabled := MainForm.ActiveConnection.IsUnicode;
+ OptionChecked := chkBulkTableEditDatabase.Checked or chkBulkTableEditEngine.Checked or chkBulkTableEditCollation.Checked
+ or chkBulkTableEditCharset.Checked or chkBulkTableEditResetAutoinc.Checked;
+ btnExecute.Enabled := SomeChecked and OptionChecked;
+ end else if tabsTools.ActivePage = tabGenerateData then begin
+ btnExecute.Caption := _('Generate');
+ btnExecute.Enabled := SomeChecked;
+ end;
+
+end;
+
+
+procedure TfrmTableTools.TreeObjectsBeforeCellPaint(Sender: TBaseVirtualTree; TargetCanvas: TCanvas;
+ Node: PVirtualNode; Column: TColumnIndex; CellPaintMode: TVTCellPaintMode; CellRect: TRect;
+ var ContentRect: TRect);
+begin
+ MainForm.DBtreeBeforeCellPaint(Sender, TargetCanvas, Node, Column, CellPaintMode, CellRect, ContentRect);
+end;
+
+procedure TfrmTableTools.TreeObjectsChange(Sender: TBaseVirtualTree; Node: PVirtualNode);
+begin
+ Mainform.DBtreeChange(Sender, Node);
+end;
+
+
+procedure TfrmTableTools.TreeObjectsChecked(Sender: TBaseVirtualTree; Node: PVirtualNode);
+var
+ Obj: PDBObject;
+begin
+ // Track sum of checked objects size
+ Obj := Sender.GetNodeData(Node);
+ if Obj.NodeType = lntDb then
+ FillTargetDatabases;
+ timerCalcSize.Enabled := False;
+ timerCalcSize.Enabled := True;
+end;
+
+
+procedure TfrmTableTools.FillTargetDatabases;
+var
+ SessionNode, DBNode: PVirtualNode;
+ OldSelected: String;
+begin
+ // Add unchecked databases
+ if comboExportOutputType.Text <> OUTPUT_DB then
+ Exit;
+ OldSelected := comboExportOutputTarget.Text;
+ comboExportOutputTarget.Items.Clear;
+ SessionNode := MainForm.GetRootNode(TreeObjects, MainForm.ActiveConnection);
+ DBNode := TreeObjects.GetFirstChild(SessionNode);
+ while Assigned(DBNode) do begin
+ if not (DBNode.CheckState in CheckedStates) then
+ comboExportOutputTarget.Items.Add(TreeObjects.Text[DBNode, 0]);
+ DBNode := TreeObjects.GetNextSibling(DBNode);
+ end;
+ comboExportOutputTarget.ItemIndex := comboExportOutputTarget.Items.IndexOf(OldSelected);
+ if comboExportOutputTarget.ItemIndex = -1 then
+ comboExportOutputTarget.ItemIndex := comboExportOutputTarget.Items.IndexOf(AppSettings.ReadString(asExportSQLDatabase));
+end;
+
+
+procedure TfrmTableTools.TreeObjectsChecking(Sender: TBaseVirtualTree; Node: PVirtualNode;
+ var NewState: TCheckState; var Allowed: Boolean);
+var
+ n: PVirtualNode;
+begin
+ // Ensure to also toggle check state of not yet initialized nodes
+ Allowed := True;
+ // Weird fix: Just iterate through all sub nodes for implicit initialization. Without this
+ // loop a checkbox click on a parent node would only auto-check its visible children.
+ n := Sender.GetFirstChild(Node);
+ while Assigned(n) do
+ n := Sender.GetNextSibling(n);
+end;
+
+
+procedure TfrmTableTools.TreeObjectsExpanded(Sender: TBaseVirtualTree;
+ Node: PVirtualNode);
+begin
+ // Auto-resizes the 2nd/"size" column
+ TreeObjectsChange(Sender, Node);
+end;
+
+procedure TfrmTableTools.TreeObjectsGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode;
+ Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: TImageIndex);
+begin
+ Mainform.DBtreeGetImageIndex(Sender, Node, Kind, Column, Ghosted, ImageIndex);
+end;
+
+
+procedure TfrmTableTools.TreeObjectsGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer);
+begin
+ MainForm.DBtreeGetNodeDataSize(Sender, NodeDataSize);
+end;
+
+procedure TfrmTableTools.TreeObjectsGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
+ Column: TColumnIndex; TextType: TVSTTextType; var CellText: String);
+begin
+ Mainform.DBtreeGetText(Sender, Node, Column, TextType, CellText);
+end;
+
+
+procedure TfrmTableTools.TreeObjectsInitChildren(Sender: TBaseVirtualTree; Node: PVirtualNode;
+ var ChildCount: Cardinal);
+begin
+ Mainform.DBtreeInitChildren(Sender, Node, ChildCount);
+end;
+
+
+procedure TfrmTableTools.TreeObjectsInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode;
+ var InitialStates: TVirtualNodeInitStates);
+begin
+ // Attach a checkbox to all nodes
+ Mainform.DBtreeInitNode(Sender, ParentNode, Node, InitialStates);
+ Node.CheckType := ctTriStateCheckBox;
+ Node.CheckState := csUncheckedNormal;
+end;
+
+
+procedure TfrmTableTools.TreeObjectsPaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas;
+ Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType);
+begin
+ Mainform.DBtreePaintText(Sender, TargetCanvas, Node, Column, TextType);
+end;
+
+
+procedure TfrmTableTools.btnHelpMaintenanceClick(Sender: TObject);
+begin
+ Mainform.CallSQLHelpWithKeyword(UpperCase(comboOperation.Text) + ' TABLE');
+end;
+
+
+function TfrmTableTools.GetCheckedObjects(DBNode: PVirtualNode): TDBObjectList;
+var
+ Child, GrandChild: PVirtualNode;
+ ChildObj, GrandChildObj: PDBObject;
+begin
+ // Return list with checked objects from database node
+ // The caller doesn't need to care whether type grouping in tree is activated
+ Result := TDBObjectList.Create(False);
+ Child := TreeObjects.GetFirstVisibleChild(DBNode);
+ while Assigned(Child) do begin
+ if Child.CheckState in CheckedStates then begin
+ ChildObj := TreeObjects.GetNodeData(Child);
+
+ case ChildObj.NodeType of
+
+ lntGroup: begin
+ GrandChild := TreeObjects.GetFirstVisibleChild(Child);
+ while Assigned(GrandChild) do begin
+ if GrandChild.CheckState in CheckedStates then begin
+ GrandChildObj := TreeObjects.GetNodeData(GrandChild);
+ Result.Add(GrandChildObj^);
+ end;
+ GrandChild := TreeObjects.GetNextVisibleSibling(GrandChild);
+ end;
+ end
+
+ else begin
+ Result.Add(ChildObj^);
+ end;
+
+ end;
+ end;
+ Child := TreeObjects.GetNextVisibleSibling(Child);
+ end;
+end;
+
+
+procedure TfrmTableTools.Execute(Sender: TObject);
+var
+ SessionNode, DBNode: PVirtualNode;
+ CheckedObjects, Triggers, Views: TDBObjectList;
+ DBObj: TDBObject;
+ i: Integer;
+ Conn: TDBConnection;
+ FileName, FileNameZip, FileNameInZip: TFileName;
+ Zip: TZipper;
+ StartTime: Cardinal;
+ LogRow: TStringlist;
+
+ procedure ProcessNode(DBObj: TDBObject);
+ begin
+ try
+ case FToolMode of
+ tmMaintenance: DoMaintenance(DBObj);
+ tmFind: DoFind(DBObj);
+ tmSQLExport: DoExport(DBObj);
+ tmBulkTableEdit: DoBulkTableEdit(DBObj);
+ tmGenerateData: DoGenerateData(DBObj);
+ end;
+ except
+ on E:EDbError do begin
+ // The above SQL can easily throw an exception, e.g. if a table is corrupted.
+ // In such cases we create a dummy row, including the error message
+ AddNotes(DBObj, 'error', E.Message);
+ // Cancel further processing on critical errors
+ if E.ErrorCode = 1049 then begin // "Unknown database"
+ ErrorDialog(E.Message);
+ FCancelled := True;
+ end;
+ end;
+ on E:EFCreateError do begin
+ // Occurs when export output file can not be created
+ ErrorDialog(E.Message);
+ FCancelled := True;
+ end;
+ on E:EInOutError do begin
+ // ForceDirectories failed with "Unable to create directory."
+ ErrorDialog(E.Message);
+ FCancelled := True;
+ end;
+ end;
+ end;
+
+begin
+ Screen.Cursor := crHourGlass;
+ // Disable critical controls so ProcessMessages is unable to do things while export is in progress
+ btnExecute.Enabled := False;
+ btnCloseOrCancel.Caption := _('Cancel');
+ btnCloseOrCancel.ModalResult := mrNone;
+ tabsTools.Enabled := False;
+ treeObjects.Enabled := False;
+ if tabsTools.ActivePage = tabMaintenance then
+ FToolMode := tmMaintenance
+ else if tabsTools.ActivePage = tabFind then
+ FToolMode := tmFind
+ else if tabsTools.ActivePage = tabSQLExport then
+ FToolMode := tmSQLExport
+ else if tabsTools.ActivePage = tabBulkTableEdit then
+ FToolMode := tmBulkTableEdit
+ else if tabsTools.ActivePage = tabGenerateData then
+ FToolMode := tmGenerateData;
+ ResultGrid.Clear;
+ ResultGrid.TrySetFocus;
+ FResults.Clear;
+ FFindSeeResultSQL.Clear;
+ Triggers := TDBObjectList.Create(False); // False, so we can .Free that object afterwards without loosing the contained objects
+ Views := TDBObjectList.Create(False);
+ FHeaderCreated := False;
+ FCancelled := False;
+
+ FObjectSizesDone := 0;
+ FObjectSizesDoneExact := 0;
+ MainForm.EnableProgress(100);
+ FStartTimeAll := GetTickCount;
+
+ DoBeforeGenerateData(Sender);
+
+ SessionNode := TreeObjects.GetFirstChild(nil);
+ while Assigned(SessionNode) do begin
+ DBNode := TreeObjects.GetFirstVisibleChild(SessionNode);
+ while Assigned(DBNode) do begin
+ if not (DBNode.CheckState in [csUncheckedNormal, csUncheckedPressed]) then begin
+ Triggers.Clear;
+ Views.Clear;
+ FSecondExportPass := False;
+ CheckedObjects := GetCheckedObjects(DBNode);
+ for DBObj in CheckedObjects do begin
+ // Triggers have to be exported at the very end
+ if (FToolMode = tmSQLExport) and (DBObj.NodeType = lntTrigger) then
+ Triggers.Add(DBObj)
+ else begin
+ ProcessNode(DBObj);
+ FObjectSizesDone := FObjectSizesDone + Max(DBObj.Size, 0);
+ FObjectSizesDoneExact := FObjectSizesDone;
+ if (FToolMode = tmSQLExport) and (DBObj.NodeType = lntView) then
+ Views.Add(DBObj);
+ end;
+ // File creation exception occurred or user clicked cancel button
+ if FCancelled then Break;
+ end; // End of db object node loop in db
+
+ // Special block for late created triggers in export mode
+ for i:=0 to Triggers.Count-1 do begin
+ ProcessNode(Triggers[i]);
+ if FCancelled then Break;
+ end;
+
+ // Special block for exporting final view structure
+ FSecondExportPass := True;
+ for i:=0 to Views.Count-1 do begin
+ ProcessNode(Views[i]);
+ if FCancelled then Break;
+ end;
+
+ end;
+ if FCancelled then Break;
+ DBNode := TreeObjects.GetNextVisibleSibling(DBNode);
+ end; // End of db item loop
+ if FCancelled then Break;
+ SessionNode := TreeObjects.GetNextSibling(SessionNode);
+ end;
+
+ Conn := Mainform.ActiveConnection;
+
+ DoAfterGenerateData(Sender);
+
+ if Assigned(ExportStream) then begin
+ // For output to file or directory:
+ Output(EXPORT_FILE_FOOTER, False, True, False, False, False);
+ // For direct output to database or server:
+ Output('/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */', True, False, False, True, True);
+ Output('/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '''') */', True, False, False, True, True);
+ Output('/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */', True, False, False, True, True);
+ Output('/*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, ''system'') */', True, False, False, True, True);
+ if comboExportOutputType.Text = OUTPUT_CLIPBOARD then
+ StreamToClipboard(ExportStream, nil);
+
+ if comboExportOutputType.Text = OUTPUT_FILE_COMPRESSED then
+ FileName := TFileStream(ExportStream).FileName;
+ FreeAndNil(ExportStream);
+
+ if comboExportOutputType.Text = OUTPUT_FILE_COMPRESSED then begin
+ AddNotes('', '', _('Compressing')+'...', '');
+ StartTime := GetTickCount;
+ FileNameZip := FExportFileName;
+ if FileExists(FileNameZip) then
+ DeleteFileWithUndo(FileNameZip);
+ Zip := TZipper.Create;
+ Zip.FileName := FileNameZip;
+ FileNameInZip := ExtractFileName(ChangeFileExt(FileNameZip, '.sql'));
+ Zip.Entries.AddFileEntry(FileName, FileNameInZip);
+ Zip.ZipAllFiles;
+ Zip.Free;
+ DeleteFile(FileName);
+ LogRow := FResults.Last;
+ LogRow[2] := _('Compressing done.');
+ LogRow[3] := FormatTimeNumber((GetTickCount-StartTime) / 1000, True);
+ UpdateResultGrid;
+ end;
+
+ // Activate ansi mode or whatever again, locally
+ Conn.Query('/*!40101 SET SQL_MODE=IFNULL(@OLD_LOCAL_SQL_MODE, '''') */');
+ // Reset timezone for reading to previous value
+ Conn.Query('/*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, ''system'') */');
+ end;
+ ExportLastDatabase := '';
+
+ for i:=0 to FModifiedDbs.Count-1 do begin
+ Conn.ClearDbObjects(FModifiedDbs[i]);
+ DBNode := MainForm.FindDBNode(TreeObjects, Conn, FModifiedDbs[i]);
+ TreeObjects.ReinitNode(DBNode, False);
+ TreeObjects.ReinitChildren(DBNode, False)
+ end;
+ FModifiedDbs.Clear;
+
+ AddNotes('', '', f_('%s finished', [tabsTools.ActivePage.Caption]), '');
+ btnCloseOrCancel.Caption := _('Close');
+ btnCloseOrCancel.ModalResult := mrCancel;
+ MainForm.ShowStatusMsg;
+ MainForm.DisableProgress;
+ tabsTools.Enabled := True;
+ treeObjects.Enabled := True;
+ ValidateControls(Sender);
+ SaveSettings(Sender);
+ Screen.Cursor := crDefault;
+end;
+
+
+procedure TfrmTableTools.DoMaintenance(DBObj: TDBObject);
+var
+ SQL: String;
+begin
+ if not (DBObj.NodeType in [lntTable, lntView]) then begin
+ AddNotes(DBObj, STRSKIPPED+'a '+LowerCase(DBObj.ObjType)+' cannot be maintained.', '');
+ Exit;
+ end;
+ SQL := UpperCase(comboOperation.Text) + ' TABLE ' + DBObj.QuotedDatabase + '.' + DBObj.QuotedName;
+ if chkQuick.Enabled and chkQuick.Checked then SQL := SQL + ' QUICK';
+ if chkFast.Enabled and chkFast.Checked then SQL := SQL + ' FAST';
+ if chkMedium.Enabled and chkMedium.Checked then SQL := SQL + ' MEDIUM';
+ if chkExtended.Enabled and chkExtended.Checked then SQL := SQL + ' EXTENDED';
+ if chkChanged.Enabled and chkChanged.Checked then SQL := SQL + ' CHANGED';
+ if chkUseFrm.Enabled and chkUseFrm.Checked then SQL := SQL + ' USE_FRM';
+ if chkForUpgrade.Enabled and chkForUpgrade.Checked then SQL := SQL + ' FOR UPGRADE';
+ AddResults(SQL, DBObj.Connection);
+end;
+
+
+procedure TfrmTableTools.editDatabaseTableFilterKeyPress(Sender: TObject;
+ var Key: Char);
+begin
+ if Key = #27 then
+ (Sender as TEditButton).OnButtonClick(Sender);
+end;
+
+procedure TfrmTableTools.editDatabaseTableFilterRightButtonClick(Sender: TObject);
+begin
+ // Click on "clear" button of any TEditButton control
+ TEditButton(Sender).Clear;
+end;
+
+procedure TfrmTableTools.editDatabaseTableFilterChange(Sender: TObject);
+var
+ Node: PVirtualNode;
+ Obj: PDBObject;
+ rxdb, rxtable: TRegExpr;
+ NodeMatches: Boolean;
+ Errors: TStringList;
+begin
+ // Immediately apply database filter
+ MainForm.LogSQL('editDatabaseTableFilterChange', lcDebug);
+
+ rxdb := TRegExpr.Create;
+ rxdb.ModifierI := True;
+ rxdb.Expression := '('+StringReplace(editDatabaseFilter.Text, ';', '|', [rfReplaceAll])+')';
+ rxtable := TRegExpr.Create;
+ rxtable.ModifierI := True;
+ rxtable.Expression := '('+StringReplace(editTableFilter.Text, ';', '|', [rfReplaceAll])+')';
+
+ Errors := TStringList.Create;
+
+ TreeObjects.BeginUpdate;
+ Node := TreeObjects.GetFirst;
+ while Assigned(Node) do begin
+ Obj := TreeObjects.GetNodeData(Node);
+ NodeMatches := True;
+ try
+ case Obj.NodeType of
+ lntDb: begin
+ // Match against database filter
+ if editDatabaseFilter.Text <> '' then
+ NodeMatches := rxdb.Exec(TreeObjects.Text[Node, 0]);
+ end;
+ lntTable..lntEvent: begin
+ // Match against table filter
+ if editTableFilter.Text <> '' then
+ NodeMatches := rxtable.Exec(TreeObjects.Text[Node, 0]);
+ // no favorites supported on table tools dialog
+ //if actFavoriteObjectsOnly.Checked then
+ // Hide non-favorite object path
+ //NodeMatches := NodeMatches and (Obj.Connection.Favorites.IndexOf(Obj.Path) > -1);
+ end;
+ end;
+ except
+ on E:Exception do begin
+ // Log regex errors, but avoid duplicate messages
+ if Errors.IndexOf(E.Message) = -1 then begin
+ MainForm.LogSQL(E.Message);
+ Errors.Add(E.Message);
+ end;
+ end;
+ end;
+ TreeObjects.IsVisible[Node] := NodeMatches;
+
+ Node := TreeObjects.GetNextInitialized(Node);
+ end;
+ TreeObjects.EndUpdate;
+
+ rxdb.Free;
+ rxtable.Free;
+
+ //editDatabaseFilter.RightButton.Visible := editDatabaseFilter.Text <> '';
+ //editTableFilter.RightButton.Visible := editTableFilter.Text <> '';
+ timerCalcSize.Enabled := False;
+ timerCalcSize.Enabled := True;
+end;
+
+
+procedure TfrmTableTools.DoFind(DBObj: TDBObject);
+var
+ Columns: TTableColumnList;
+ Col: TTableColumn;
+ SQL, Column, RoutineDefinitionColumn, RoutineSchemaColumn, FindText, FindTextJokers: String;
+ IsRegExp: Boolean;
+
+ function esc(Value: String): String;
+ begin
+ Result := DBObj.Connection.EscapeString(Value);
+ end;
+begin
+ FFindSeeResultSQL.Add('');
+
+ FindText := memoFindText.Text;
+ case comboMatchType.ItemIndex of
+ 0,4: FindTextJokers := '%'+FindText+'%'; // Used as wildcard for regex on MSSQL
+ 1: FindTextJokers := FindText;
+ 2: FindTextJokers := '%'+FindText;
+ 3: FindTextJokers := FindText+'%';
+ end;
+ IsRegExp := comboMatchType.ItemIndex = 4;
+
+ RoutineDefinitionColumn := DBObj.Connection.QuoteIdent('routine_definition');
+ if not chkCaseSensitive.Checked then begin
+ FindText := LowerCase(FindText);
+ FindTextJokers := LowerCase(FindTextJokers);
+ RoutineDefinitionColumn := 'LOWER('+RoutineDefinitionColumn+')';
+ if DBObj.Connection.Parameters.IsAnySQLite then begin
+ DBObj.Connection.Query('PRAGMA case_sensitive_like=FALSE');
+ end;
+ end else begin
+ if DBObj.Connection.Parameters.IsAnySQLite then begin
+ DBObj.Connection.Query('PRAGMA case_sensitive_like=TRUE');
+ end;
+ end;
+ RoutineSchemaColumn := 'routine_schema';
+ if DBObj.Connection.Parameters.IsAnyMSSQL then
+ RoutineSchemaColumn := 'routine_catalog';
+
+ Columns := TTableColumnList.Create(True);
+ case DBObj.NodeType of
+ lntTable, lntView: Columns := DBObj.TableColumns;
+ lntProcedure, lntFunction: ;
+ // TODO: Triggers + Events
+ else AddNotes(DBObj, STRSKIPPED+'a '+LowerCase(DBObj.ObjType)+' does not contain rows.', '');
+ end;
+ case DBObj.NodeType of
+ lntTable, lntView: begin
+ if Columns.Count > 0 then begin
+ SQL := '';
+ for Col in Columns do begin
+ Column := DBObj.Connection.QuoteIdent(Col.Name);
+ if (comboDatatypes.ItemIndex = 0) or (Integer(Col.DataType.Category) = comboDatatypes.ItemIndex-1) then begin
+
+ if tabsTextType.ActivePage = tabSQL then begin
+ SQL := SQL + Column + ' ' + SynMemoFindText.Text + ' OR ';
+
+ end else if (Col.DataType.Category in [dtcInteger, dtcReal]) and (comboMatchType.ItemIndex=1) then begin
+ // Search numbers
+ SQL := SQL + Column + '=' + UnformatNumber(FindText) + ' OR ';
+
+ end else if chkCaseSensitive.Checked then begin
+ // Search case sensitive
+ case DBObj.Connection.Parameters.NetTypeGroup of
+ ngMySQL: begin
+ if IsRegExp then
+ SQL := SQL + Column + ' REGEXP BINARY ' + esc(FindText) + ' OR '
+ else
+ SQL := SQL + Column + ' LIKE BINARY ' + esc(FindTextJokers) + ' OR ';
+ end;
+ ngMSSQL:
+ SQL := SQL + Column+' LIKE ' + esc(FindTextJokers) + ' COLLATE SQL_Latin1_General_CP1_CS_AS OR ';
+ ngPgSQL: begin
+ if IsRegExp then
+ SQL := SQL + 'CAST(' + Column + ' AS TEXT) SIMILAR TO ' + esc(FindTextJokers) + ' OR '
+ else
+ SQL := SQL + 'CAST(' + Column + ' AS TEXT) LIKE ' + esc(FindTextJokers) + ' OR ';
+ end;
+ ngSQLite: begin
+ if IsRegExp then
+ SQL := SQL + Column + ' REGEXP ' + esc(FindText) + ' OR '
+ else
+ SQL := SQL + Column + ' LIKE ' + esc(FindTextJokers) + ' OR ';
+ end;
+ end;
+
+ end else begin
+ // Search case insensitive
+ case DBObj.Connection.Parameters.NetTypeGroup of
+ ngMySQL: begin
+ if IsRegExp then
+ SQL := SQL + 'LOWER(CONVERT('+Column+' USING '+DBObj.Connection.CharacterSet+')) REGEXP ' + esc(FindText) + ' OR '
+ else
+ SQL := SQL + 'LOWER(CONVERT('+Column+' USING '+DBObj.Connection.CharacterSet+')) LIKE ' + esc(FindTextJokers) + ' OR ';
+ end;
+ ngMSSQL: begin
+ SQL := SQL + 'LOWER('+Column+') LIKE ' + esc(FindTextJokers) + ' OR ';
+ end;
+ ngPgSQL: begin
+ if IsRegExp then
+ SQL := SQL + 'LOWER(CAST('+Column+' AS TEXT)) SIMILAR TO ' + esc(FindTextJokers) + ' OR '
+ else
+ SQL := SQL + 'CAST('+Column+' AS TEXT) ILIKE ' + esc(FindTextJokers) + ' OR ';
+ end;
+ ngSQLite: begin
+ if IsRegExp then
+ SQL := SQL + Column + ' REGEXP ' + esc(FindText) + ' OR '
+ else
+ SQL := SQL + Column + ' LIKE ' + esc(FindTextJokers) + ' OR ';
+ end;
+ end;
+ end;
+
+ end;
+ end; // end of loop over columns
+
+ if SQL <> '' then begin
+ Delete(SQL, Length(SQL)-3, 3);
+ FFindSeeResultSQL[FFindSeeResultSQL.Count-1] := 'SELECT * FROM '+DBObj.QuotedDatabase+'.'+DBObj.QuotedName+' WHERE ' + SQL;
+ case DBObj.Connection.Parameters.NetTypeGroup of
+ ngMySQL, ngPgSQL:
+ SQL := 'SELECT '''+DBObj.Database+''' AS '+DBObj.Connection.QuoteIdent('Database')+', '''+DBObj.Name+''' AS '+DBObj.Connection.QuoteIdent('Table')+', COUNT(*) AS '+DBObj.Connection.QuoteIdent('Found rows')+', '
+ + 'CONCAT(ROUND(100 / '+IntToStr(Max(DBObj.Rows,1))+' * COUNT(*), 1), ''%'') AS '+DBObj.Connection.QuoteIdent('Relevance')+' FROM '+DBObj.QuotedDatabase+'.'+DBObj.QuotedName+' WHERE '
+ + SQL;
+ ngMSSQL:
+ SQL := 'SELECT '''+DBObj.Database+''' AS '+DBObj.Connection.QuoteIdent('Database')+', '''+DBObj.Name+''' AS '+DBObj.Connection.QuoteIdent('Table')+', COUNT(*) AS '+DBObj.Connection.QuoteIdent('Found rows')+', '
+ + 'CONVERT(VARCHAR(10), ROUND(100 / '+IntToStr(Max(DBObj.Rows,1))+' * COUNT(*), 1)) + ''%'' AS '+DBObj.Connection.QuoteIdent('Relevance')+' FROM '+DBObj.QuotedDatabase+'.'+DBObj.QuotedName+' WHERE '
+ + SQL;
+ ngSQLite:
+ SQL := 'SELECT '''+DBObj.Database+''' AS '+DBObj.Connection.QuoteIdent('Database')+', '''+DBObj.Name+''' AS '+DBObj.Connection.QuoteIdent('Table')+', COUNT(*) AS '+DBObj.Connection.QuoteIdent('Found rows')+', '
+ + '(ROUND(100 / '+IntToStr(Max(DBObj.Rows,1))+' * COUNT(*), 1) || ''%'') AS '+DBObj.Connection.QuoteIdent('Relevance')+' FROM '+DBObj.QuotedDatabase+'.'+DBObj.QuotedName+' WHERE '
+ + SQL;
+ end;
+ AddResults(SQL, DBObj.Connection);
+ end else begin
+ // Prefer a normal log line, so the "Found rows" column has a number, to fix wrong sorting
+ //AddNotes(DBObj, f_('%s%s doesn''t have columns of selected type (%s).', [STRSKIPPED, DBObj.ObjType, comboDatatypes.Text]), '');
+ AddNotes(DBObj.Database, DBObj.Name, '0', '0%');
+ end;
+ end;
+ end;
+
+ lntProcedure, lntFunction: begin
+ SQL := 'SELECT '+
+ esc(DBObj.Database)+' AS '+DBObj.Connection.QuoteIdent('Database')+', '+
+ esc(DBObj.Name)+' AS '+DBObj.Connection.QuoteIdent('Table')+', '+
+ DBObj.Connection.GetSQLSpecifity(spFuncCeil)+'(('+DBObj.Connection.GetSQLSpecifity(spFuncLength)+'('+RoutineDefinitionColumn+') - '+DBObj.Connection.GetSQLSpecifity(spFuncLength)+'(REPLACE('+RoutineDefinitionColumn+', '+esc(FindText)+', '+esc('')+'))) / '+DBObj.Connection.GetSQLSpecifity(spFuncLength)+'('+esc(FindText)+')) AS '+DBObj.Connection.QuoteIdent('Found rows')+', '+
+ '0 AS '+DBObj.Connection.QuoteIdent('Relevance')+
+ 'FROM '+DBObj.Connection.QuoteIdent(DBObj.Connection.InfSch)+'.'+DBObj.Connection.QuoteIdent('routines')+' '+
+ 'WHERE '+DBObj.Connection.QuoteIdent(RoutineSchemaColumn)+'='+esc(DBObj.Database)+' AND '+DBObj.Connection.QuoteIdent('routine_name')+'='+esc(DBObj.Name);
+ AddResults(SQL, DBObj.Connection);
+ end;
+
+ end;
+
+ Columns.Free;
+end;
+
+
+procedure TfrmTableTools.btnSeeResultsClick(Sender: TObject);
+var
+ SQL: String;
+ i: Integer;
+ Tab: TQueryTab;
+begin
+ // "See results" clicked - auto create new query tab, and execute a batch of SELECT queries
+ SQL := '';
+ for i:=0 to FResults.Count-1 do begin
+ if MakeInt(FResults[i][2]) > 0 then begin
+ SQL := SQL + FFindSeeResultSQL[i] + ';' + CRLF;
+ end;
+ end;
+ MainForm.actNewQueryTab.Execute;
+ Tab := MainForm.QueryTabs[MainForm.QueryTabs.Count-1];
+ Tab.Memo.Text := SQL;
+ Tab.TabSheet.Show;
+ MainForm.actExecuteQueryExecute(Sender);
+end;
+
+
+procedure TfrmTableTools.AddResults(SQL: String; Connection: TDBConnection);
+var
+ i: Integer;
+ Row: TStringList;
+ Results: TDBQuery;
+ Value: String;
+begin
+ // Execute query and append results into grid
+ Results := Connection.GetResults(SQL);
+ Connection.ShowWarnings;
+ if Results = nil then
+ Exit;
+
+ SetupResultGrid(Results);
+ Results.First;
+ while not Results.Eof do begin
+ Row := TStringList.Create;
+ for i:=0 to Results.ColumnCount-1 do begin
+ Value := Results.Col(i);
+ if Results.DataType(i).Category = dtcInteger then begin
+ if MakeFloat(Value) >= 0 then
+ Row.Add(FormatNumber(Value))
+ else
+ Row.Add('');
+ end else
+ Row.Add(Value);
+ end;
+ FResults.Add(Row);
+ Results.Next;
+ end;
+ Results.Free;
+
+ UpdateResultGrid;
+end;
+
+
+procedure TfrmTableTools.AddNotes(Col1, Col2, Col3, Col4: String);
+var
+ Row: TStringList;
+begin
+ // Adds a row with non SQL results
+ SetupResultGrid;
+ Row := TStringList.Create;
+ Row.Add(Col1);
+ Row.Add(Col2);
+ Row.Add(Col3);
+ Row.Add(Col4);
+ FResults.Add(Row);
+ UpdateResultGrid;
+end;
+
+
+procedure TfrmTableTools.AddNotes(DBObject: TDBObject; Msg1, Msg2: String);
+begin
+ AddNotes(DBObject.Database, DBObject.Name, Msg1, Msg2);
+end;
+
+
+procedure TfrmTableTools.SetupResultGrid(Results: TDBQuery=nil);
+var
+ ColCount, i: Integer;
+ Col: TVirtualTreeColumn;
+begin
+ if Assigned(Results) then begin
+ ColCount := Results.ColumnCount;
+ ResultGrid.Header.Options := ResultGrid.Header.Options + [hoVisible];
+ end else begin
+ ColCount := 4;
+ // Remove column headers if this is the first row
+ if FResults.Count = 0 then
+ ResultGrid.Header.Options := ResultGrid.Header.Options - [hoVisible];
+ end;
+
+ // Add missing columns
+ for i:=ResultGrid.Header.Columns.Count to ColCount-1 do begin
+ Col := ResultGrid.Header.Columns.Add;
+ Col.Width := 130;
+ end;
+ // Remove superfluous columns
+ for i:=ResultGrid.Header.Columns.Count-1 downto ColCount do
+ ResultGrid.Header.Columns[i].Free;
+
+ // Set column header names
+ for i:=0 to ResultGrid.Header.Columns.Count-1 do begin
+ Col := ResultGrid.Header.Columns[i];
+ if Assigned(Results) then begin
+ Col.Text := Results.ColumnNames[i];
+ if Results.DataType(i).Category in [dtcInteger, dtcReal] then
+ Col.Alignment := taRightJustify
+ else
+ Col.Alignment := taLeftJustify;
+ end;
+ end;
+end;
+
+
+procedure TfrmTableTools.spltHorizontallyMoved(Sender: TObject);
+begin
+ editDatabaseFilter.Left := 0;
+ editDatabaseFilter.Width := (pnlLeftTop.Width div 2) - 1;
+ editTableFilter.Width := editDatabaseFilter.Width;
+ editTableFilter.Left := editDatabaseFilter.Width + 1;
+end;
+
+procedure TfrmTableTools.timerCalcSizeTimer(Sender: TObject);
+var
+ SessionNode, DBNode: PVirtualNode;
+ CheckedObjects: TDBObjectList;
+ DBObj: TDBObject;
+begin
+ // Calculate object sizes and display on label
+ timerCalcSize.Enabled := False;
+ SessionNode := TreeObjects.GetFirstChild(nil);
+ FObjectSizes := 0;
+ while Assigned(SessionNode) do begin
+ DBNode := TreeObjects.GetFirstVisibleChild(SessionNode);
+ while Assigned(DBNode) do begin
+ if not (DBNode.CheckState in [csUncheckedNormal, csUncheckedPressed]) then begin
+ CheckedObjects := GetCheckedObjects(DBNode);
+ for DBObj in CheckedObjects do begin
+ Inc(FObjectSizes, DBObj.Size);
+ end;
+ end;
+ DBNode := TreeObjects.GetNextVisibleSibling(DBNode);
+ end;
+ SessionNode := TreeObjects.GetNextVisibleSibling(SessionNode);
+ end;
+ ValidateControls(Sender);
+end;
+
+procedure TfrmTableTools.UpdateResultGrid;
+var
+ Percent: Double;
+begin
+ // Refresh resultgrid
+ ResultGrid.RootNodeCount := FResults.Count;
+ ResultGrid.FocusedNode := ResultGrid.GetLast;
+ ResultGrid.Selected[ResultGrid.FocusedNode] := True;
+ Percent := 100 / Max(FObjectSizes,1) * FObjectSizesDoneExact;
+ Percent := Min(Percent, 100);
+ lblCheckedSize.Caption := f_('Selected objects size: %s', [FormatByteNumber(FObjectSizes)]) + '. ' +
+ f_('%s%% done', [FormatNumber(Percent, 1)]) + '.';
+ MainForm.SetProgressPosition(Round(Percent));
+ MainForm.ShowStatusMsg(Format(StatusMsg, [tabsTools.ActivePage.Caption, FormatTimeNumber((GetTickCount-FStartTimeAll)/1000, True)]));
+ ResultGrid.Header.AutoFitColumns(False);
+ Application.ProcessMessages;
+end;
+
+procedure TfrmTableTools.ResultGridCompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode;
+ Column: TColumnIndex; var Result: Integer);
+begin
+ Mainform.AnyGridCompareNodes(Sender, Node1, Node2, Column, Result);
+end;
+
+procedure TfrmTableTools.ResultGridGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer);
+begin
+ NodeDataSize := SizeOf(TStringList);
+end;
+
+
+procedure TfrmTableTools.ResultGridInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode;
+ var InitialStates: TVirtualNodeInitStates);
+var
+ Data: ^TStringList;
+begin
+ // Bind string list to node
+ Data := Sender.GetNodeData(Node);
+ Data^ := FResults[Node.Index];
+end;
+
+
+procedure TfrmTableTools.ResultGridPaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas;
+ Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType);
+var
+ VT: TLazVirtualStringTree;
+ Msg: String;
+begin
+ // Red text color for errors, purple for notes, grey for skipped tables
+ if not (vsSelected in Node.States) then begin
+ VT := Sender as TLazVirtualStringTree;
+ Msg := VT.Text[Node, 2];
+ if LowerCase(Msg) = 'note' then
+ TargetCanvas.Font.Color := clPurple
+ else if LowerCase(Msg) = 'error' then
+ TargetCanvas.Font.Color := clRed
+ else if Pos(STRSKIPPED, Msg) > 0 then
+ TargetCanvas.Font.Color := GetThemeColor(clGrayText);
+ end;
+end;
+
+procedure TfrmTableTools.ResultGridGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
+ TextType: TVSTTextType; var CellText: String);
+var
+ Data: ^TStringList;
+begin
+ if Column > NoColumn then begin
+ Data := Sender.GetNodeData(Node);
+ if Data^.Count > Column then
+ CellText := Data^[Column]
+ else
+ CellText := '';
+ end;
+end;
+
+
+procedure TfrmTableTools.ResultGridHeaderClick(Sender: TVTHeader; HitInfo: TVTHeaderHitInfo);
+begin
+ // Header column clicked to sort
+ Mainform.AnyGridHeaderClick(Sender, HitInfo);
+end;
+
+
+procedure TfrmTableTools.comboExportOutputTypeChange(Sender: TObject);
+var
+ SessionName, FilenameHint: String;
+ Params: TConnectionParameters;
+ Placeholders: TStringList;
+ i: Integer;
+begin
+ // Target type (file, directory, ...) selected
+ comboExportOutputTarget.Enabled := True;
+ comboExportOutputTarget.Text := '';
+ comboExportOutputTarget.Hint := '';
+
+ // Create filename placeholders hint
+ Placeholders := GetOutputFilenamePlaceholders;
+ FilenameHint := _('Allows the following replacement patterns:');
+ for i:=0 to Placeholders.Count-1 do begin
+ FilenameHint := FilenameHint + CRLF + '%' + Placeholders.Names[i] + ': ' + Placeholders.ValueFromIndex[i];
+ end;
+ Placeholders.Free;
+
+ if Assigned(FTargetConnection) then
+ FreeAndNil(FTargetConnection);
+ if (comboExportOutputType.Text = OUTPUT_FILE)
+ or (comboExportOutputType.Text = OUTPUT_FILE_COMPRESSED) then begin
+ comboExportOutputTarget.Style := csDropDown;
+ comboExportOutputTarget.Hint := FilenameHint;
+ if comboExportOutputType.Text = OUTPUT_FILE then
+ comboExportOutputTarget.Items.Text := AppSettings.ReadString(asExportSQLFilenames, '')
+ else
+ comboExportOutputTarget.Items.Text := AppSettings.ReadString(asExportZIPFilenames, '');
+ if comboExportOutputTarget.Items.Count > 0 then begin
+ comboExportOutputTarget.ItemIndex := 0;
+ // Cut long file list down to 20 latest items
+ for i:=comboExportOutputTarget.Items.Count-1 downto 20 do begin
+ comboExportOutputTarget.Items.Delete(i);
+ end;
+ end;
+ lblExportOutputTarget.Caption := _('Filename')+':';
+ btnExportOutputTargetSelect.Enabled := True;
+ btnExportOutputTargetSelect.ImageIndex := 51;
+ end else if comboExportOutputType.Text = OUTPUT_DIR then begin
+ comboExportOutputTarget.Style := csDropDown;
+ comboExportOutputTarget.Hint := FilenameHint;
+ comboExportOutputTarget.Items.Text := AppSettings.ReadString(asExportSQLDirectories, '');
+ if comboExportOutputTarget.Items.Count > 0 then
+ comboExportOutputTarget.ItemIndex := 0;
+ lblExportOutputTarget.Caption := _('Directory')+':';
+ btnExportOutputTargetSelect.Enabled := True;
+ btnExportOutputTargetSelect.ImageIndex := 51;
+ end else if comboExportOutputType.Text = OUTPUT_CLIPBOARD then begin
+ comboExportOutputTarget.Enabled := False;
+ comboExportOutputTarget.Items.Clear;
+ lblExportOutputTarget.Caption := '';
+ btnExportOutputTargetSelect.Enabled := False;
+ btnExportOutputTargetSelect.ImageIndex := 4;
+ end else if comboExportOutputType.Text = OUTPUT_DB then begin
+ comboExportOutputTarget.Style := csDropDownList;
+ lblExportOutputTarget.Caption := _('Database')+':';
+ btnExportOutputTargetSelect.Enabled := False;
+ btnExportOutputTargetSelect.ImageIndex := 27;
+ FillTargetDatabases;
+ end else begin
+ // Server selected. Display databases in below dropdown
+ comboExportOutputTarget.Style := csDropDownList;
+ lblExportOutputTarget.Caption := _('Database')+':';
+ btnExportOutputTargetSelect.Enabled := False;
+ btnExportOutputTargetSelect.ImageIndex := 27;
+ SessionName := Copy(comboExportOutputType.Text, Length(OUTPUT_SERVER)+1, Length(comboExportOutputType.Text));
+ FreeAndNil(FTargetConnection);
+ Params := TConnectionParameters.Create(SessionName);
+ FTargetConnection := Params.CreateConnection(Self);
+ FTargetConnection.LogPrefix := SessionName;
+ FTargetConnection.OnLog := Mainform.LogSQL;
+ Screen.Cursor := crHourglass;
+ try
+ FTargetConnection.Active := True;
+ comboExportOutputTarget.Items := FTargetConnection.AllDatabases;
+ comboExportOutputTarget.Items.Insert(0, '['+_('Same as on source server')+']');
+ comboExportOutputTarget.ItemIndex := comboExportOutputTarget.Items.IndexOf(AppSettings.ReadString(asExportSQLServerDatabase));
+ if comboExportOutputTarget.ItemIndex = -1 then
+ comboExportOutputTarget.ItemIndex := 0;
+ Screen.Cursor := crDefault;
+ except
+ on E:EDbError do begin
+ Screen.Cursor := crDefault;
+ ErrorDialog(E.Message);
+ comboExportOutputType.ItemIndex := FLastOutputSelectedIndex;
+ comboExportOutputType.OnChange(Sender);
+ end;
+ end;
+ end;
+
+ FLastOutputSelectedIndex := comboExportOutputType.ItemIndex;
+ chkExportDatabasesCreate.Enabled :=
+ (comboExportOutputType.Text = OUTPUT_FILE)
+ or (comboExportOutputType.Text = OUTPUT_FILE_COMPRESSED)
+ or (comboExportOutputType.Text = OUTPUT_CLIPBOARD)
+ or (Copy(comboExportOutputType.Text, 1, Length(OUTPUT_SERVER)) = OUTPUT_SERVER);
+ chkExportDatabasesDrop.Enabled := chkExportDatabasesCreate.Enabled;
+ ValidateControls(Sender);
+end;
+
+
+procedure TfrmTableTools.comboExportOutputTargetChange(Sender: TObject);
+begin
+ ValidateControls(Sender);
+end;
+
+
+procedure TfrmTableTools.chkExportOptionClick(Sender: TObject);
+ procedure WarnIfChecked(chk: TCheckBox; LabelText: String);
+ begin
+ if chk.Checked then begin
+ chk.Caption := LabelText + '!!';
+ chk.Font.Style := chk.Font.Style + [fsBold];
+ end else begin
+ chk.Caption := LabelText;
+ chk.Font.Style := Font.Style;
+ end;
+ end;
+begin
+ if (Sender = chkExportDatabasesDrop) and chkExportDatabasesDrop.Checked then
+ chkExportDatabasesCreate.Checked := True
+ else if (Sender = chkExportDatabasesCreate) and (not chkExportDatabasesCreate.Checked) then
+ chkExportDatabasesDrop.Checked := False
+ else if (Sender = chkExportTablesDrop) and chkExportTablesDrop.Checked then
+ chkExportTablesCreate.Checked := True
+ else if (Sender = chkExportTablesCreate) and (not chkExportTablesCreate.Checked) then
+ chkExportTablesDrop.Checked := False;
+ WarnIfChecked(chkExportDatabasesDrop, _('Drop'));
+ WarnIfChecked(chkExportTablesDrop, _('Drop'));
+end;
+
+
+procedure TfrmTableTools.btnCloseOrCancelClick(Sender: TObject);
+begin
+ // Set cancel flag to stop running loop in the next possible loop position
+ if TButton(Sender).ModalResult = mrNone then begin
+ FCancelled := True;
+ Mainform.LogSQL(_('Processing cancelled by user, waiting for current object to finish ...'), lcInfo);
+ end;
+end;
+
+
+procedure TfrmTableTools.btnExportOptionsClick(Sender: TObject);
+begin
+ ShowPopup(Sender as TButton, popupExportOptions);
+end;
+
+
+procedure TfrmTableTools.btnExportOutputTargetSelectClick(Sender: TObject);
+var
+ SaveDialog: TSaveDialog;
+ Browse: TSelectDirectoryDialog;
+begin
+ if (comboExportOutputType.Text = OUTPUT_FILE) or (comboExportOutputType.Text = OUTPUT_FILE_COMPRESSED) then begin
+ // Select filename
+ SaveDialog := TSaveDialog.Create(Self);
+ SaveDialog.DefaultExt := 'sql';
+ if comboExportOutputType.Text = OUTPUT_FILE then
+ SaveDialog.Filter := _('SQL files')+' (*.sql)|*.sql|'+_('All files')+' (*.*)|*.*'
+ else
+ SaveDialog.Filter := _('ZIP files')+' (*.zip)|*.zip|'+_('All files')+' (*.*)|*.*';
+ // Don't prompt here if file exists, but later when exporting starts. See issue #835
+ // SaveDialog.Options := SaveDialog.Options + [ofOverwritePrompt];
+ if SaveDialog.Execute then
+ comboExportOutputTarget.Text := SaveDialog.FileName;
+ SaveDialog.Free;
+ end else if(comboExportOutputType.Text = OUTPUT_DIR) then begin
+ Browse := TSelectDirectoryDialog.Create(Self);
+ Browse.InitialDir := comboExportOutputTarget.Text;
+ Browse.Title := _('Select output directory');
+ // Enable "Create new folder" button
+ //Browse.BrowseOptions := Browse.BrowseOptions - [bifNoNewFolderButton] + [bifNewDialogStyle];
+ if Browse.Execute then
+ comboExportOutputTarget.Text := Browse.FileName;
+ Browse.Free;
+ end;
+ ValidateControls(Sender);
+end;
+
+procedure TfrmTableTools.SetToolMode(Value: TToolMode);
+begin
+ FToolMode := Value;
+ case FToolMode of
+ tmMaintenance: tabsTools.ActivePage := tabMaintenance;
+ tmFind: tabsTools.ActivePage := tabFind;
+ tmSQLExport: tabsTools.ActivePage := tabSQLExport;
+ tmBulkTableEdit: tabsTools.ActivePage := tabBulkTableEdit;
+ tmGenerateData: tabsTools.ActivePage := tabGenerateData;
+ end;
+end;
+
+
+// Pass output to file or query, and append semicolon if needed
+procedure TfrmTableTools.Output(SQL: String; IsEndOfQuery, ForFile, ForDir, ForDb, ForServer: Boolean);
+var
+ SA: AnsiString;
+ ChunkSize: Integer;
+begin
+ if (ToFile and ForFile) or (ToDir and ForDir) or (ToClipboard and ForFile) then begin
+ if IsEndOfQuery then
+ SQL := SQL + ';'+CRLF;
+ StreamWrite(ExportStream, SQL);
+ if IsEndOfQuery then
+ ExportStreamStartOfQueryPos := ExportStream.Size;
+ end;
+ if (ToDb and ForDb) or (ToServer and ForServer) then begin
+ StreamWrite(ExportStream, SQL);
+ if IsEndOfQuery then begin
+ ExportStream.Position := 0;
+ ChunkSize := ExportStream.Size;
+ SetLength(SA, ChunkSize div SizeOf(AnsiChar));
+ ExportStream.Read(PAnsiChar(SA)^, ChunkSize);
+ ExportStream.Size := 0;
+ ExportStreamStartOfQueryPos := 0;
+ SQL := UTF8ToString(SA);
+ if ToDB then MainForm.ActiveConnection.Query(SQL, False, lcScript)
+ else if ToServer then FTargetConnection.Query(SQL, False, lcScript);
+ SQL := '';
+ end;
+ end;
+end;
+
+
+procedure TfrmTableTools.DoExport(DBObj: TDBObject);
+var
+ NeedsDBStructure: Boolean;
+ InsertSizeExceeded, RowLimitExceeded: Boolean;
+ Struc, Header, DbDir, FinalDbName, BaseInsert, Row, TargetDbAndObject, BinContent, tmp: String;
+ i: Integer;
+ RowCount, RowCountInChunk: Int64;
+ Limit, Offset, ResultCount, MaxInsertSize: Int64;
+ StartTime: Cardinal;
+ StrucResult, Data: TDBQuery;
+ ColumnList: TTableColumnList;
+ Column: TTableColumn;
+ Quoter: TDBConnection;
+ TargetFileName, SetCharsetCode: String;
+const
+ TempDelim = '//';
+ AssumedAvgRowLen = 10000;
+
+ procedure LogStatistic(RowsDone: Int64);
+ var
+ LogRow: TStringlist;
+ Percent: Double;
+ BytesDone: Int64;
+ begin
+ LogRow := FResults.Last;
+ Percent := 100 / Max(DBObj.Rows,1) * Max(RowsDone,1);
+ Percent := Min(Percent, 100);
+ BytesDone := Max(DBObj.Size,0) div Max(DBObj.Rows,1) * RowsDone;
+ FObjectSizesDoneExact := FObjectSizesDone + BytesDone;
+ LogRow[2] := FormatNumber(RowsDone) + ' / ' + FormatNumber(Percent, 0)+'%';
+ LogRow[3] := FormatTimeNumber((GetTickCount-StartTime) / 1000, True);
+ UpdateResultGrid;
+ end;
+
+begin
+ // Handle one table, view or whatever in SQL export mode
+ AddResults('SELECT '+DBObj.Connection.EscapeString(DBObj.Database)+' AS '+DBObj.Connection.QuoteIdent('Database')+', ' +
+ DBObj.Connection.EscapeString(DBObj.Name)+' AS '+DBObj.Connection.QuoteIdent('Table')+', ' +
+ IntToStr(DBObj.Rows)+' AS '+DBObj.Connection.QuoteIdent('Rows')+', '+
+ '0 AS '+DBObj.Connection.QuoteIdent('Duration')
+ , DBObj.Connection
+ );
+ ToFile := (comboExportOutputType.Text = OUTPUT_FILE) or (comboExportOutputType.Text = OUTPUT_FILE_COMPRESSED);
+ ToDir := comboExportOutputType.Text = OUTPUT_DIR;
+ ToClipboard := comboExportOutputType.Text = OUTPUT_CLIPBOARD;
+ ToDb := comboExportOutputType.Text = OUTPUT_DB;
+ ToServer := Copy(comboExportOutputType.Text, 1, Length(OUTPUT_SERVER)) = OUTPUT_SERVER;
+ if not Assigned(ExportStream) then begin
+ // Very first round here. Prevent "SHOW CREATE db|table" from using double quotes
+ DBObj.Connection.Query('/*!40101 SET @OLD_LOCAL_SQL_MODE=@@SQL_MODE, SQL_MODE='''' */');
+ // Set same timezone for reading date/time values as for the output
+ DBObj.Connection.Query('/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */');
+ DBObj.Connection.Query('/*!40103 SET TIME_ZONE=''+00:00'' */');
+ end;
+
+ if ToServer then
+ Quoter := FTargetConnection
+ else
+ Quoter := DBObj.Connection;
+
+ StartTime := GetTickCount;
+ ExportStreamStartOfQueryPos := 0;
+ MaxInsertSize := Trunc(StrToInt64Def(editInsertSize.Text, 0) * SIZE_KB * 0.9);
+
+ if ToDir then begin
+ FreeAndNil(ExportStream);
+ DbDir := IncludeTrailingPathDelimiter(GetOutputFilename(comboExportOutputTarget.Text, DBObj)) + DBObj.Database + '\';
+ if not DirectoryExists(DbDir) then
+ ForceDirectories(DbDir);
+ ExportStream := TFileStream.Create(DbDir + DBObj.Name+'.sql', fmCreate or fmOpenWrite);
+ FHeaderCreated := False;
+ end;
+ if not Assigned(ExportStream) then begin
+ if ToFile then begin
+ TargetFileName := GetOutputFilename(comboExportOutputTarget.Text, DBObj);
+ if FileExists(TargetFileName) then begin
+ case MessageDialog(f_('File already exists: %s'+sLineBreak+sLineBreak+'Overwrite it?', [TargetFileName]), mtConfirmation, [mbYes, mbCancel]) of
+ mrYes:;
+ mrCancel:
+ raise EFCreateError.CreateFmt(_('Export cancelled, file not overwritten: %s'), [TargetFileName]);
+ end;
+ end;
+
+ FExportFileName := TargetFileName;
+ if comboExportOutputType.Text = OUTPUT_FILE_COMPRESSED then
+ TargetFileName := ChangeFileExt(TargetFileName, '_temp.sql');
+ if not IsValidFilePath(TargetFileName) then
+ raise EFCreateError.CreateFmt(_('Filename or path contains illegal characters: "%s"'), [TargetFilename]);
+ if not DirectoryExists(ExtractFilePath(FExportFileName)) then
+ ForceDirectories(ExtractFilePath(FExportFileName));
+ ExportStream := TFileStream.Create(TargetFileName, fmCreate or fmOpenWrite);
+ end;
+ // ToDir handled above
+ if ToClipboard then
+ ExportStream := TMemoryStream.Create;
+ if ToDb or ToServer then
+ ExportStream := TMemoryStream.Create;
+ end;
+ if not FHeaderCreated then begin
+ // For output to file or directory:
+ if DBObj.Connection.CharacterSet = 'utf8mb4' then
+ SetCharsetCode := '/*!40101 SET NAMES utf8 */;' + CRLF +
+ '/*!50503 SET NAMES '+DBObj.Connection.CharacterSet+' */;' + CRLF
+ else
+ SetCharsetCode := '/*!40101 SET NAMES '+DBObj.Connection.CharacterSet+' */;' + CRLF;
+ Header := '';
+ if menuExportAddComments.Checked then begin
+ Header := Header +
+ '-- --------------------------------------------------------' + CRLF +
+ Format('-- %-30s%s', [_('Host')+':', DBObj.Connection.Parameters.HostName]) + CRLF +
+ Format('-- %-30s%s', [_('Server version')+':', DBObj.Connection.ServerVersionUntouched]) + CRLF +
+ Format('-- %-30s%s', [_('Server OS')+':', DBObj.Connection.ServerOS]) + CRLF +
+ Format('-- %-30s%s', [APPNAME + ' ' + _('Version')+':', Mainform.AppVersion]) + CRLF +
+ '-- --------------------------------------------------------' + CRLF + CRLF;
+ end;
+ Header := Header +
+ '/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;' + CRLF +
+ SetCharsetCode +
+ '/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;' + CRLF +
+ '/*!40103 SET TIME_ZONE=''+00:00'' */;' + CRLF +
+ '/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;' + CRLF +
+ '/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE=''NO_AUTO_VALUE_ON_ZERO'' */;' + CRLF +
+ '/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;' + CRLF;
+ Output(Header, False, DBObj.Database<>ExportLastDatabase, True, False, False);
+ Output(CRLF, False, True, True, False, False);
+
+ // For direct output to database or server:
+ Output('/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */', True, False, False, True, True);
+ Output('/*!40103 SET TIME_ZONE=''+00:00'' */', True, False, False, True, True);
+ Output('/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */', True, False, False, True, True);
+ Output('/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE=''NO_AUTO_VALUE_ON_ZERO'' */', True, False, False, True, True);
+ Output('/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */', True, False, False, True, True);
+
+ FHeaderCreated := True;
+ end;
+
+ // Database structure. Do that only in single-file and server mode. drop/create/use in directory or database mode makes no sense
+ FinalDbName := DBObj.Database;
+ if ToDb or (ToServer and (comboExportOutputTarget.ItemIndex > 0)) then
+ FinalDbName := comboExportOutputTarget.Text;
+ NeedsDBStructure := FinalDbName <> ExportLastDatabase;
+ if chkExportDatabasesDrop.Checked or chkExportDatabasesCreate.Checked then begin
+ if menuExportAddComments.Checked then
+ Output(CRLF+'-- '+f_('Dumping database structure for %s', [DBObj.Database])+CRLF, False, NeedsDBStructure, False, False, False);
+ if chkExportDatabasesDrop.Checked and chkExportDatabasesDrop.Enabled then
+ Output('DROP DATABASE IF EXISTS '+Quoter.QuoteIdent(FinalDbName), True, NeedsDBStructure, False, False, NeedsDBStructure);
+ if chkExportDatabasesCreate.Checked and chkExportDatabasesCreate.Enabled then begin
+ if DBObj.Connection.ServerVersionInt >= 40100 then begin
+ Struc := DBObj.Connection.GetVar('SHOW CREATE DATABASE '+DBObj.QuotedDatabase, 1);
+ // Gracefully ignore it when target database exists, important in server mode
+ Insert('IF NOT EXISTS ', Struc, Pos('DATABASE', Struc) + 9);
+ // Create the right dbname
+ Struc := StringReplace(Struc, DBObj.Database, FinalDbName, []);
+ end else
+ Struc := 'CREATE DATABASE IF NOT EXISTS '+Quoter.QuoteIdent(FinalDbName);
+ Output(Struc, True, NeedsDBStructure, False, False, NeedsDBStructure);
+ Output(Quoter.GetSQLSpecifity(spUSEQuery, [Quoter.QuoteIdent(FinalDbName)]), True, NeedsDBStructure, False, False, NeedsDBStructure);
+ Output(CRLF, False, NeedsDBStructure, False, False, NeedsDBStructure);
+ end;
+ end;
+ if ToServer and (not chkExportDatabasesCreate.Checked) then begin
+ // Export to server without "CREATE/USE dbname" and "Same dbs as on source server" - needs a "USE dbname"
+ Output(Quoter.GetSQLSpecifity(spUSEQuery, [Quoter.QuoteIdent(FinalDbName)]), True, False, False, False, NeedsDBStructure);
+ end;
+
+ // Table structure
+ if chkExportTablesDrop.Checked or chkExportTablesCreate.Checked then begin
+ if menuExportAddComments.Checked and (not FSecondExportPass) then
+ Output('-- '+f_('Dumping structure for %s %s.%s', [_(LowerCase(DBObj.ObjType)), DBObj.Database, DBObj.Name])+CRLF, False, True, True, False, False);
+ if chkExportTablesDrop.Checked and (not FSecondExportPass) then begin
+ Struc := 'DROP '+UpperCase(DBObj.ObjType)+' IF EXISTS ';
+ if ToDb then
+ Struc := Struc + Quoter.QuoteIdent(FinalDbName)+'.';
+ Struc := Struc + Quoter.QuoteIdent(DBObj.Name);
+ Output(Struc, True, True, True, True, True);
+ end;
+ if chkExportTablesCreate.Checked then begin
+ try
+ case DBObj.NodeType of
+ lntTable: begin
+ Struc := DBObj.GetCreateCode(menuExportRemoveAutoIncrement.Checked, False);
+ Insert('IF NOT EXISTS ', Struc, Pos('TABLE', Struc) + 6);
+ if ToDb then
+ Insert(Quoter.QuoteIdent(FinalDbName)+'.', Struc, Pos('EXISTS', Struc) + 7 );
+ if ToServer then
+ Struc := TSqlTranspiler.CreateTable(Struc, DBObj.Connection, FTargetConnection);
+ end;
+
+ lntView: begin
+ if not FSecondExportPass then begin
+ // Create temporary VIEW replacement
+ ColumnList := DBObj.TableColumns;
+ Struc := '';
+ if menuExportAddComments.Checked then
+ Struc := Struc + '-- '+_('Creating temporary table to overcome VIEW dependency errors')+CRLF;
+ Struc := Struc + 'CREATE TABLE ';
+ if ToDb then
+ Struc := Struc + Quoter.QuoteIdent(FinalDbName) + '.';
+ Struc := Struc + Quoter.QuoteIdent(DBObj.Name)+' (';
+ for Column in ColumnList do begin
+ // Prevent DEFAULT value from coming in, to fix errors due to multiple CURRENT_TIMESTAMP values
+ // See issue #2748
+ Column.DefaultType := cdtNothing;
+ if Column.DataType.Index = dbdtVarchar then
+ Column.LengthSet := '1';
+ Struc := Struc + sLineBreak + CodeIndent + Column.SQLCode + ',';
+ end;
+ Delete(Struc, Length(Struc), 1);
+ Struc := Struc + CRLF + ') ENGINE=MyISAM';
+ ColumnList.Free;
+ end else begin
+ Struc := '';
+ if menuExportAddComments.Checked then
+ Struc := Struc + '-- '+_('Removing temporary table and create final VIEW structure')+CRLF;
+ Struc := Struc + 'DROP TABLE IF EXISTS ';
+ if ToDb then
+ Struc := Struc + Quoter.QuoteIdent(FinalDbName)+'.';
+ Struc := Struc + Quoter.QuoteIdent(DBObj.Name);
+ Output(Struc, True, True, True, True, True);
+ Struc := DBObj.GetCreateCode(False, menuExportRemoveDefiner.Checked);
+ if ToDb then
+ Insert(Quoter.QuoteIdent(FinalDbName)+'.', Struc, Pos('VIEW', Struc) + 5 );
+ // Issue #2050: Add new line at end to support comments at the end of a view definition
+ Struc := Struc + sLineBreak;
+ end;
+ end;
+
+ lntTrigger: begin
+ StrucResult := DBObj.Connection.GetResults('SHOW TRIGGERS FROM '+DBObj.QuotedDatabase+' WHERE `Trigger`='+DBObj.Connection.EscapeString(DBObj.Name));
+ Struc := DBObj.GetCreateCode(False, menuExportRemoveDefiner.Checked);
+ if ToDb then
+ Insert(Quoter.QuoteIdent(FinalDbName)+'.', Struc, Pos('TRIGGER', Struc) + 8 );
+ if ToFile or ToClipboard or ToDir then begin
+ Struc := 'SET @OLDTMP_SQL_MODE=@@SQL_MODE, SQL_MODE=' + DBObj.Connection.EscapeString(StrucResult.Col('sql_mode')) + ';' + CRLF +
+ 'DELIMITER ' + TempDelim + CRLF +
+ Struc + TempDelim + CRLF +
+ 'DELIMITER ;' + CRLF +
+ 'SET SQL_MODE=@OLDTMP_SQL_MODE';
+ end;
+ end;
+
+ lntFunction, lntProcedure: begin
+ Struc := DBObj.GetCreateCode(False, menuExportRemoveDefiner.Checked);
+ if ToDb then begin
+ if DBObj.NodeType = lntProcedure then
+ Insert(Quoter.QuoteIdent(FinalDbName)+'.', Struc, Pos('PROCEDURE', Struc) + 10 )
+ else if DBObj.NodeType = lntFunction then
+ Insert(Quoter.QuoteIdent(FinalDbName)+'.', Struc, Pos('FUNCTION', Struc) + 9 );
+ end;
+ // Change delimiter for file output, so readers split queries at the right string position
+ if ToFile or ToDir or ToClipboard then
+ Struc := 'DELIMITER ' + TempDelim + CRLF + Struc + TempDelim + CRLF + 'DELIMITER ';
+ end;
+
+ lntEvent: begin
+ Struc := DBObj.GetCreateCode(False, menuExportRemoveDefiner.Checked);
+ if ToDb then
+ Insert(Quoter.QuoteIdent(FinalDbName)+'.', Struc, Pos('EVENT', Struc) + 6 );
+ if ToFile or ToDir or ToClipboard then
+ Struc := 'DELIMITER ' + TempDelim + CRLF + Struc + TempDelim + CRLF + 'DELIMITER ';
+ end;
+ end;
+ Struc := fixNewlines(Struc);
+ Output(Struc, True, True, True, True, True);
+ Output(CRLF, False, True, True, True, True);
+ except
+ on E:EDbError do begin
+ // Catch the exception message and dump it into the export file for debugging reasons
+ Output('/* '+E.Message+' */', False, True, True, False, False);
+ Raise;
+ end;
+ end;
+ end;
+ end;
+
+ if DBObj.NodeType = lntTable then begin
+ // Table data
+ if comboExportData.Text = DATA_NO then begin
+ if menuExportAddComments.Checked then
+ Output('-- '+_('Data exporting was unselected.')+CRLF+CRLF, False, True, True, False, False);
+ end else if MatchText(DBObj.Engine, ['MRG_MYISAM', 'FEDERATED']) then begin
+ if menuExportAddComments.Checked then
+ Output('-- '+f_('Table data not exported because this is a %s table which holds its data in separate tables.', [DBObj.Engine])+CRLF+CRLF, False, True, True, False, False);
+ end else begin
+ tmp := FormatNumber(DBObj.Rows)+' rows';
+ if LowerCase(DBObj.Engine) = 'innodb' then
+ tmp := '~'+tmp+' ('+_('approximately')+')';
+ if menuExportAddComments.Checked then
+ Output('-- '+f_('Dumping data for table %s.%s: %s', [DBObj.Database, DBObj.Name, tmp])+CRLF, False, True, True, False, False);
+ TargetDbAndObject := Quoter.QuoteIdent(DBObj.Name);
+ if ToDb then
+ TargetDbAndObject := Quoter.QuoteIdent(FinalDbName) + '.' + TargetDbAndObject;
+ Offset := 0;
+ RowCount := 0;
+ // Calculate limit so we select ~100MB per loop
+ // Take care of disabled "Get full table status" session setting, where AvgRowLen is 0
+ Limit := Round(100 * SIZE_MB / IfThen(DBObj.AvgRowLen>0, DBObj.AvgRowLen, AssumedAvgRowLen));
+ if comboExportData.Text = DATA_REPLACE then
+ Output('DELETE FROM '+TargetDbAndObject, True, True, True, True, True);
+ if DBObj.Engine.ToLowerInvariant <> 'innodb' then begin
+ Output('/*!40000 ALTER TABLE '+TargetDbAndObject+' DISABLE KEYS */', True, True, True, True, True);
+ end;
+ while true do begin
+ Data := DBObj.Connection.GetResults(
+ DBObj.Connection.ApplyLimitClause(
+ 'SELECT',
+ '* FROM '+DBObj.QuotedDbAndTableName,
+ Limit,
+ Offset)
+ );
+ Inc(Offset, Limit);
+ if Data.RecordCount = 0 then
+ break;
+ if FCancelled then
+ Break;
+ Data.PrepareColumnAttributes;
+ BaseInsert := 'INSERT INTO ';
+ if comboExportData.Text = DATA_INSERTNEW then
+ BaseInsert := 'INSERT IGNORE INTO '
+ else if comboExportData.Text = DATA_UPDATE then
+ BaseInsert := 'REPLACE INTO ';
+ BaseInsert := BaseInsert + TargetDbAndObject + ' (';
+ for i:=0 to Data.ColumnCount-1 do begin
+ if not Data.ColIsVirtual(i) then
+ BaseInsert := BaseInsert + Quoter.QuoteIdent(Data.ColumnNames[i]) + ', ';
+ end;
+ Delete(BaseInsert, Length(BaseInsert)-1, 2);
+ BaseInsert := BaseInsert + ') VALUES' + sLineBreak + CodeIndent + '(';
+ while true do begin
+ Output(BaseInsert, False, True, True, True, True);
+ RowCountInChunk := 0;
+
+ while not Data.Eof do begin
+ Row := '';
+ if RowCountInChunk > 0 then
+ Row := Row + ',' + sLineBreak + CodeIndent + '(';
+ for i:=0 to Data.ColumnCount-1 do begin
+ if Data.ColIsVirtual(i) then
+ Continue;
+ if Data.IsNull(i) then
+ Row := Row + 'NULL'
+ else case Data.DataType(i).Category of
+ dtcInteger, dtcReal: begin
+ if Data.DataType(i).Index = dbdtBit then
+ Row := Row + 'b' + Quoter.EscapeString(Data.Col(i))
+ else
+ Row := Row + Data.Col(i);
+ end;
+ dtcBinary, dtcSpatial: begin
+ BinContent := Data.HexValue(i);
+ if Length(BinContent) > 0 then
+ Row := Row + '_binary ' + BinContent
+ else
+ Row := Row + Quoter.EscapeString('');
+ end;
+ else Row := Row + Quoter.EscapeString(Data.Col(i));
+ end;
+ Row := Row + ', ';
+ end;
+ Delete(Row, Length(Row)-1, 2);
+ Row := Row + ')';
+ // Break if stream would increase over the barrier of 1MB, and throw away current row
+ InsertSizeExceeded := ExportStream.Size - ExportStreamStartOfQueryPos + Length(Row) > MaxInsertSize;
+ // Same with MSSQL which is limited to 1000 rows per INSERT
+ RowLimitExceeded := RowCountInChunk >= Quoter.MaxRowsPerInsert;
+ if (RowCountInChunk > 0) and (InsertSizeExceeded or RowLimitExceeded or FCancelled) then
+ Break;
+ Inc(RowCount);
+ Inc(RowCountInChunk);
+ Output(Row, False, True, True, True, True);
+ Data.Next;
+ end;
+ Output('', True, True, True, True, True);
+ LogStatistic(RowCount);
+ if Data.Eof or FCancelled then
+ break;
+
+ end;
+ ResultCount := Data.RecordCount;
+ FreeAndNil(Data);
+ // Break if end of table data, avoid one last empty/useless SELECT in next loop
+ if ResultCount < Limit then
+ break;
+
+ end;
+ if DBObj.Engine.ToLowerInvariant <> 'innodb' then begin
+ Output('/*!40000 ALTER TABLE '+TargetDbAndObject+' ENABLE KEYS */', True, True, True, True, True);
+ end;
+ Output(CRLF, False, True, True, True, True);
+ // Cosmetic fix for estimated InnoDB row count
+ DBObj.Rows := RowCount;
+ LogStatistic(RowCount);
+ end;
+ end;
+
+ // Add footer in directory mode after each item
+ Output(EXPORT_FILE_FOOTER, False, False, True, False, False);
+
+ ExportLastDatabase := FinalDbName;
+end;
+
+
+procedure TfrmTableTools.chkBulkTableEditCheckComboClick(Sender: TObject);
+var
+ chk: TCheckBox;
+ combo: TWinControl;
+begin
+ chk := TCheckBox(Sender);
+ if chk = chkBulkTableEditDatabase then combo := comboBulkTableEditDatabase
+ else if chk = chkBulkTableEditEngine then combo := comboBulkTableEditEngine
+ else if chk = chkBulkTableEditCollation then combo := comboBulkTableEditCollation
+ else combo := comboBulkTableEditCharset;
+ combo.Enabled := chk.Checked;
+ ValidateControls(Sender);
+end;
+
+
+procedure TfrmTableTools.DoBulkTableEdit(DBObj: TDBObject);
+var
+ Specs, LogRow: TStringList;
+ CreateView: String;
+ rx: TRegExpr;
+ HasCharsetClause: Boolean;
+ SelectedCharset: String;
+begin
+ AddResults('SELECT '+DBObj.Connection.EscapeString(DBObj.Database)+' AS '+DBObj.Connection.QuoteIdent('Database')+', ' +
+ DBObj.Connection.EscapeString(DBObj.Name)+' AS '+DBObj.Connection.QuoteIdent('Table')+', ' +
+ DBObj.Connection.EscapeString('Updating...')+' AS '+DBObj.Connection.QuoteIdent('Operation')+', '+
+ ''''' AS '+DBObj.Connection.QuoteIdent('Result')
+ , DBObj.Connection
+ );
+ Specs := TStringList.Create;
+ if chkBulkTableEditDatabase.Checked and (comboBulkTableEditDatabase.Text <> DBObj.Database) then begin
+ case DBObj.NodeType of
+ lntTable: Specs.Add('RENAME ' + DBObj.Connection.QuoteIdent(comboBulkTableEditDatabase.Text)+'.'+DBObj.QuotedName);
+ lntView: begin
+ // Although RENAME works for views, that does not work for moving to another database without getting
+ // a "Changing schema from x to y is not allowed". Instead, recreate them manually
+ CreateView := DBObj.CreateCode;
+ rx := TRegExpr.Create;
+ rx.ModifierI := True;
+ // Replace old database references in VIEW body
+ rx.Expression := '(["`])'+QuoteRegExprMetaChars(DBObj.Database)+'(["`])';
+ CreateView := rx.Replace(CreateView, DBObj.Connection.QuoteIdent(comboBulkTableEditDatabase.Text), false);
+ rx.Free;
+ // Temporarily switch to new database for VIEW creation, so the database references are correct
+ DBObj.Connection.Database := comboBulkTableEditDatabase.Text;
+ DBObj.Connection.Query(CreateView);
+ DBObj.Connection.Database := DBObj.Database;
+ DBObj.Connection.Query('DROP VIEW '+DBObj.QuotedName);
+ end;
+ end;
+ if FModifiedDbs.IndexOf(DBObj.Database) = -1 then
+ FModifiedDbs.Add(DBObj.Database);
+ if FModifiedDbs.IndexOf(comboBulkTableEditDatabase.Text) = -1 then
+ FModifiedDbs.Add(comboBulkTableEditDatabase.Text);
+ end;
+ if (DBObj.NodeType = lntTable) and chkBulkTableEditEngine.Checked then begin
+ if MainForm.ActiveConnection.ServerVersionInt < 40018 then
+ Specs.Add('TYPE '+comboBulkTableEditEngine.Text)
+ else
+ Specs.Add('ENGINE '+comboBulkTableEditEngine.Text);
+ end;
+ if DBObj.NodeType = lntTable then begin
+ HasCharsetClause := False;
+ if chkBulkTableEditCharset.Checked and (comboBulkTableEditCharset.ItemIndex > -1) then begin
+ SelectedCharset := RegExprGetMatch('^(\w+)\b', comboBulkTableEditCharset.Text, 1);
+ Specs.Add('CONVERT TO CHARSET '+SelectedCharset);
+ HasCharsetClause := True;
+ end;
+ if chkBulkTableEditCollation.Checked and (comboBulkTableEditCollation.ItemIndex > -1) then begin
+ if HasCharsetClause then // No comma between charset + collation clause
+ Specs[Specs.Count-1] := Specs[Specs.Count-1] + ' COLLATE '+DBObj.Connection.EscapeString(comboBulkTableEditCollation.Text)
+ else
+ Specs.Add('COLLATE '+DBObj.Connection.EscapeString(comboBulkTableEditCollation.Text));
+ end;
+ if chkBulkTableEditResetAutoinc.Checked then
+ Specs.Add('AUTO_INCREMENT=0');
+ end;
+
+ LogRow := FResults.Last;
+ if Specs.Count > 0 then begin
+ DBObj.Connection.Query('ALTER TABLE ' + DBObj.QuotedDatabase + '.' + DBObj.QuotedName + ' ' + Implode(', ', Specs));
+ LogRow[2] := _('Done');
+ LogRow[3] := _('Success');
+ end else begin
+ LogRow[2] := _('Nothing to do');
+ LogRow[3] := f_('Selected operations cannot be applied to a %s', [_(LowerCase(DBObj.ObjType))]);
+ end;
+ UpdateResultGrid;
+end;
+
+
+procedure TfrmTableTools.DoBeforeGenerateData(Sender: TObject);
+var
+ Conn: TDBConnection;
+begin
+ // Disable foreign key checks
+ if ToolMode <> tmGenerateData then
+ Exit;
+ Conn := MainForm.ActiveConnection;
+ if Conn.Has(frForeignKeyChecksVar) then
+ Conn.Query('SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0');
+end;
+
+
+procedure TfrmTableTools.DoAfterGenerateData(Sender: TObject);
+var
+ Conn: TDBConnection;
+begin
+ // Disable foreign key checks
+ if ToolMode <> tmGenerateData then
+ Exit;
+ Conn := MainForm.ActiveConnection;
+ if Conn.Has(frForeignKeyChecksVar) then
+ Conn.Query('SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1)');
+end;
+
+
+procedure TfrmTableTools.DoGenerateData(DBObj: TDBObject);
+var
+ Columns: TTableColumnList;
+ Col: TTableColumn;
+ InsertSqlBase, InsertSql: String;
+ ColumnNamesSkipped, ColumnNamesQuoted, Values: TStringList;
+ i, j: Integer;
+ IntVal, MaxLen, MinLen: Integer;
+ FloatVal: Extended;
+ JsonText: TJSONString;
+ EnumValues: TStringList;
+ TextVal: String;
+ BinVal: String;
+begin
+ // Generate rows
+ if not (DBObj.NodeType in [lntTable, lntView]) then begin
+ AddNotes(DBObj, STRSKIPPED+'cannot insert rows in a '+LowerCase(DBObj.ObjType), '');
+ Exit;
+ end;
+ AddNotes(DBObj, 'Inserting '+FormatNumber(editGenerateDataNumRows.Text)+' rows into '+DBObj.Name, '');
+ UpdateResultGrid;
+
+ Columns := DBObj.TableColumns;
+
+ InsertSqlBase := 'INSERT INTO ' + DBObj.QuotedDbAndTableName + ' ';
+ ColumnNamesQuoted := TStringList.Create;
+ ColumnNamesSkipped := TStringList.Create;
+ Values := TStringList.Create;
+ for Col in Columns do begin
+ if Col.DefaultType = cdtAutoInc then begin
+ ColumnNamesSkipped.Add(Col.Name);
+ Continue;
+ end;
+ if (Col.DefaultType = cdtExpression) and ExecRegExprI('^(NOW()|CURRENT_TIMESTAMP)', Col.DefaultText) then begin
+ ColumnNamesSkipped.Add(Col.Name);
+ Continue;
+ end;
+
+ ColumnNamesQuoted.Add(Col.Connection.QuoteIdent(Col.Name));
+ end;
+ InsertSqlBase := InsertSqlBase + '(' + Implode(', ', ColumnNamesQuoted) + ') VALUES ';
+
+ Randomize;
+
+ for i:=1 to StrToInt64Def(editGenerateDataNumRows.Text, 0) do begin
+ Values.Clear;
+ // Generate random values. Include some NULLs for columns which allow that.
+ for Col in Columns do begin
+ if ColumnNamesSkipped.IndexOf(Col.Name) > -1 then
+ Continue;
+
+ // https://www.delphipraxis.net/31059-warscheinlichkeit-random.html
+ if Col.AllowNull
+ and (StrToInt64Def(editGenerateDataNullAmount.Text, 0) > 0) // prevent division by zero
+ and (Random < (StrToInt64Def(editGenerateDataNullAmount.Text, 0) / 100))
+ then begin
+ Values.Add('NULL');
+ Continue;
+ end;
+
+ case Col.DataType.Category of
+ dtcInteger: begin
+ // Take care of overflow in RandomRange with signed integers
+ IntVal := 0;
+ case Col.DataType.Index of
+ dbdtTinyint:
+ IntVal := IfThen(Col.Unsigned, RandomRange(0, 256), RandomRange(-128, 128));
+ dbdtSmallint:
+ IntVal := IfThen(Col.Unsigned, RandomRange(0, 65535), RandomRange(-32768, 32768));
+ dbdtMediumint:
+ IntVal := IfThen(Col.Unsigned, RandomRange(0, 16777215), RandomRange(-8388608, 8388608));
+ dbdtUint:
+ IntVal := RandomRange(0, MaxInt);
+ dbdtInt, dbdtBigint:
+ IntVal := IfThen(Col.Unsigned, RandomRange(0, MaxInt), RandomRange(0 - MaxInt, MaxInt));
+ end;
+ Values.Add(IntVal.ToString);
+ end;
+
+ dtcReal: begin
+ FloatVal := 0;
+ case Col.DataType.Index of
+ dbdtFloat, dbdtDouble, dbdtDecimal, dbdtNumeric, dbdtReal, dbdtDoublePrecision, dbdtMoney, dbdtSmallmoney:
+ FloatVal := IfThen(Col.Unsigned, RandomRange(0, 100000), RandomRange(-100000, 100000)) + Random;
+ end;
+ Values.Add(FloatToStr(FloatVal, MainForm.FormatSettings));
+ end;
+
+ dtcText: begin
+ MaxLen := 0;
+ case Col.DataType.Index of
+ dbdtChar, dbdtVarchar:
+ MaxLen := StrToIntDef(Col.LengthSet, 1);
+ dbdtTinytext:
+ MaxLen := Trunc(Power(2, 8)) -1;
+ dbdtText, dbdtMediumtext, dbdtLongtext, dbdtJson, dbdtJsonB:
+ MaxLen := Trunc(Power(2, 16)) -1;
+ end;
+ TextVal := '';
+ MaxLen := RandomRange(1, MaxLen+1);
+ for j:=1 to MaxLen do begin
+ // Only printable characters
+ TextVal := TextVal + Chr(RandomRange(32, 127));
+ end;
+ if Col.DataType.Index in [dbdtJson, dbdtJsonB] then begin
+ JsonText := TJSONString.Create(TextVal);
+ TextVal := JsonText.AsJSON;
+ JsonText.Free;
+ end;
+ Values.Add(Col.Connection.EscapeString(TextVal));
+ end;
+
+ dtcBinary: begin
+ MaxLen := 0;
+ case Col.DataType.Index of
+ dbdtBinary, dbdtVarbinary:
+ MaxLen := StrToIntDef(Col.LengthSet, 1);
+ dbdtTinyblob:
+ MaxLen := Trunc(Power(2, 8)) -1;
+ dbdtBlob, dbdtMediumblob, dbdtLongblob:
+ MaxLen := Trunc(Power(2, 16)) -1;
+ end;
+ BinVal := '';
+ MinLen := Min(16, MaxLen);
+ MaxLen := RandomRange(MinLen, MaxLen+1);
+ for j:=1 to MaxLen do begin
+ BinVal := BinVal + Chr(RandomRange(1, 256));
+ end;
+ Values.Add(Col.Connection.EscapeBin(BinVal));
+ end;
+
+ dtcTemporal: begin
+ TextVal := '';
+ case Col.DataType.Index of
+ dbdtDate, dbdtTime, dbdtYear, dbdtDatetime, dbdtDatetime2, dbdtTimestamp, dbdtInterval: begin
+ MinLen := Trunc(VarToDateTime('1971-01-01'));
+ MaxLen := Trunc(VarToDateTime('2035-01-01'));
+ FloatVal := RandomRange(MinLen, MaxLen) + Random;
+ TextVal := FormatDateTime(Col.DataType.Format, FloatVal, MainForm.FormatSettings);
+ end;
+
+ dbdtDatetimeOffset: ;
+ dbdtSmalldatetime: ;
+ end;
+ Values.Add(Col.Connection.EscapeString(TextVal));
+ end;
+
+ dtcSpatial: ;
+
+ dtcOther: begin
+ case Col.DataType.Index of
+ dbdtEnum, dbdtSet: begin
+ EnumValues := Col.ValueList;
+ IntVal := RandomRange(0, EnumValues.Count);
+ TextVal := EnumValues[IntVal];
+ EnumValues.Free;
+ Values.Add(Col.Connection.EscapeString(TextVal));
+ end;
+ dbdtBool: begin
+ IntVal := RandomRange(0, 2);
+ TextVal := IfThen(IntVal=0, 'true', 'false');
+ Values.Add(Col.Connection.EscapeString(TextVal));
+ end;
+ else
+ Values.Add('0');
+ end;
+ end;
+ end;
+ end;
+ InsertSql := InsertSqlBase + '(' + Implode(', ', Values) + ')';
+ DBObj.Connection.Query(InsertSql, False, lcScript);
+ end;
+
+ ColumnNamesQuoted.Free;
+ ColumnNamesSkipped.Free;
+ Values.Free;
+end;
+
+
+procedure TfrmTableTools.CheckAllClick(Sender: TObject);
+var
+ DBNode, ObjNode: PVirtualNode;
+ WantedType: TListNodeType;
+ DBObj: PDBObject;
+ CheckNone: Boolean;
+ CheckedNodes: Int64;
+begin
+ // Check all/none/by type via context menu
+ WantedType := TListNodeType((Sender as TMenuItem).Tag);
+ CheckNone := Sender = menuCheckNone;
+ case TreeObjects.GetNodeLevel(TreeObjects.FocusedNode) of
+ 1: DBNode := TreeObjects.FocusedNode;
+ 2: DBNode := TreeObjects.FocusedNode.Parent;
+ 3: DBNode := TreeObjects.FocusedNode.Parent.Parent;
+ else raise Exception.Create(_('Unhandled tree level'));
+ end;
+ ObjNode := TreeObjects.GetFirstChild(DBNode);
+ CheckedNodes := 0;
+ while Assigned(ObjNode) do begin
+ DBObj := TreeObjects.GetNodeData(ObjNode);
+ if CheckNone then
+ TreeObjects.CheckState[ObjNode] := csUncheckedNormal
+ else begin
+ if (WantedType = lntNone) or (DBObj.NodeType = WantedType) or (DBObj.GroupType = WantedType) then
+ TreeObjects.CheckState[ObjNode] := csCheckedNormal
+ else
+ TreeObjects.CheckState[ObjNode] := csUncheckedNormal;
+ end;
+ if ObjNode.CheckState = csCheckedNormal then
+ Inc(CheckedNodes);
+ TreeObjects.RepaintNode(ObjNode);
+ ObjNode := TreeObjects.GetNextSibling(ObjNode);
+ end;
+ if CheckedNodes = 0 then
+ TreeObjects.CheckState[DBNode] := csUncheckedNormal
+ else if CheckedNodes = TreeObjects.ChildCount[DBNode] then
+ TreeObjects.CheckState[DBNode] := csCheckedNormal
+ else
+ TreeObjects.CheckState[DBNode] := csMixedNormal;
+end;
+
+
+end.